agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v1 7/9] regress: Check for postgres startup completion more often
80+ messages / 3 participants
[nested] [flat]

* [PATCH v3 10/10] regress: Check for postgres startup completion more often
@ 2023-08-07 23:51 Andres Freund <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Andres Freund @ 2023-08-07 23:51 UTC (permalink / raw)

Previously pg_regress.c only checked whether the server started up once a
second - in most cases startup is much faster though. Use the same interval as
pg_ctl does.
---
 src/test/regress/pg_regress.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 407e3915cec..46af1fddbdb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -75,6 +75,9 @@ const char *pretty_diff_opts = "-w -U3";
  */
 #define TESTNAME_WIDTH 36
 
+/* how often to recheck if postgres startup completed */
+#define WAITS_PER_SEC	10
+
 typedef enum TAPtype
 {
 	DIAG = 0,
@@ -2499,7 +2502,7 @@ regression_main(int argc, char *argv[],
 		else
 			wait_seconds = 60;
 
-		for (i = 0; i < wait_seconds; i++)
+		for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
 		{
 			/* Done if psql succeeds */
 			fflush(NULL);
@@ -2519,7 +2522,7 @@ regression_main(int argc, char *argv[],
 					 outputdir);
 			}
 
-			pg_usleep(1000000L);
+			pg_usleep(1000000L / WAITS_PER_SEC);
 		}
 		if (i >= wait_seconds)
 		{
-- 
2.38.0


--uh2yukyzfvojbe2k--





^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 10/10] regress: Check for postgres startup completion more often
@ 2023-08-07 23:51 Andres Freund <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Andres Freund @ 2023-08-07 23:51 UTC (permalink / raw)

Previously pg_regress.c only checked whether the server started up once a
second - in most cases startup is much faster though. Use the same interval as
pg_ctl does.
---
 src/test/regress/pg_regress.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 407e3915cec..46af1fddbdb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -75,6 +75,9 @@ const char *pretty_diff_opts = "-w -U3";
  */
 #define TESTNAME_WIDTH 36
 
+/* how often to recheck if postgres startup completed */
+#define WAITS_PER_SEC	10
+
 typedef enum TAPtype
 {
 	DIAG = 0,
@@ -2499,7 +2502,7 @@ regression_main(int argc, char *argv[],
 		else
 			wait_seconds = 60;
 
-		for (i = 0; i < wait_seconds; i++)
+		for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
 		{
 			/* Done if psql succeeds */
 			fflush(NULL);
@@ -2519,7 +2522,7 @@ regression_main(int argc, char *argv[],
 					 outputdir);
 			}
 
-			pg_usleep(1000000L);
+			pg_usleep(1000000L / WAITS_PER_SEC);
 		}
 		if (i >= wait_seconds)
 		{
-- 
2.38.0


--uh2yukyzfvojbe2k--





^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v1 7/9] regress: Check for postgres startup completion more often
@ 2023-08-07 23:51 Andres Freund <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Andres Freund @ 2023-08-07 23:51 UTC (permalink / raw)

Previously pg_regress.c only checked whether the server started up once a
second - in most cases startup is much faster though. Use the same interval as
pg_ctl does.
---
 src/test/regress/pg_regress.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 407e3915cec..46af1fddbdb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -75,6 +75,9 @@ const char *pretty_diff_opts = "-w -U3";
  */
 #define TESTNAME_WIDTH 36
 
+/* how often to recheck if postgres startup completed */
+#define WAITS_PER_SEC	10
+
 typedef enum TAPtype
 {
 	DIAG = 0,
@@ -2499,7 +2502,7 @@ regression_main(int argc, char *argv[],
 		else
 			wait_seconds = 60;
 
-		for (i = 0; i < wait_seconds; i++)
+		for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
 		{
 			/* Done if psql succeeds */
 			fflush(NULL);
@@ -2519,7 +2522,7 @@ regression_main(int argc, char *argv[],
 					 outputdir);
 			}
 
-			pg_usleep(1000000L);
+			pg_usleep(1000000L / WAITS_PER_SEC);
 		}
 		if (i >= wait_seconds)
 		{
-- 
2.38.0


--uu4yojthqnm7ulmd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0008-ci-Don-t-specify-amount-of-memory.patch"



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v1 7/9] regress: Check for postgres startup completion more often
@ 2023-08-07 23:51 Andres Freund <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Andres Freund @ 2023-08-07 23:51 UTC (permalink / raw)

Previously pg_regress.c only checked whether the server started up once a
second - in most cases startup is much faster though. Use the same interval as
pg_ctl does.
---
 src/test/regress/pg_regress.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 407e3915cec..46af1fddbdb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -75,6 +75,9 @@ const char *pretty_diff_opts = "-w -U3";
  */
 #define TESTNAME_WIDTH 36
 
+/* how often to recheck if postgres startup completed */
+#define WAITS_PER_SEC	10
+
 typedef enum TAPtype
 {
 	DIAG = 0,
@@ -2499,7 +2502,7 @@ regression_main(int argc, char *argv[],
 		else
 			wait_seconds = 60;
 
-		for (i = 0; i < wait_seconds; i++)
+		for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
 		{
 			/* Done if psql succeeds */
 			fflush(NULL);
@@ -2519,7 +2522,7 @@ regression_main(int argc, char *argv[],
 					 outputdir);
 			}
 
-			pg_usleep(1000000L);
+			pg_usleep(1000000L / WAITS_PER_SEC);
 		}
 		if (i >= wait_seconds)
 		{
-- 
2.38.0


--uu4yojthqnm7ulmd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0008-ci-Don-t-specify-amount-of-memory.patch"



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v1 7/9] regress: Check for postgres startup completion more often
@ 2023-08-07 23:51 Andres Freund <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Andres Freund @ 2023-08-07 23:51 UTC (permalink / raw)

Previously pg_regress.c only checked whether the server started up once a
second - in most cases startup is much faster though. Use the same interval as
pg_ctl does.
---
 src/test/regress/pg_regress.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 407e3915cec..46af1fddbdb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -75,6 +75,9 @@ const char *pretty_diff_opts = "-w -U3";
  */
 #define TESTNAME_WIDTH 36
 
+/* how often to recheck if postgres startup completed */
+#define WAITS_PER_SEC	10
+
 typedef enum TAPtype
 {
 	DIAG = 0,
@@ -2499,7 +2502,7 @@ regression_main(int argc, char *argv[],
 		else
 			wait_seconds = 60;
 
-		for (i = 0; i < wait_seconds; i++)
+		for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
 		{
 			/* Done if psql succeeds */
 			fflush(NULL);
@@ -2519,7 +2522,7 @@ regression_main(int argc, char *argv[],
 					 outputdir);
 			}
 
-			pg_usleep(1000000L);
+			pg_usleep(1000000L / WAITS_PER_SEC);
 		}
 		if (i >= wait_seconds)
 		{
-- 
2.38.0


--uu4yojthqnm7ulmd
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
	filename="v1-0008-ci-Don-t-specify-amount-of-memory.patch"



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v1 2/3] Move x86 popcount code to pg_popcount_x86_64.c.
@ 2026-01-14 17:54 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-01-14 17:54 UTC (permalink / raw)

This moves the SSE4.2 popcount implementations to the recently
renamed file for x86-64-specific popcount code.
---
 src/include/port/pg_bitutils.h |  20 ++-
 src/port/pg_bitutils.c         | 263 +-------------------------------
 src/port/pg_popcount_x86_64.c  | 264 +++++++++++++++++++++++++++++++--
 3 files changed, 267 insertions(+), 280 deletions(-)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 8ed12f7a021..c9c508d4ba3 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -307,23 +307,21 @@ pg_ceil_log2_64(uint64 num)
 #define POPCNT_AARCH64 1
 #endif
 
+extern int	pg_popcount32_slow(uint32 word);
+extern int	pg_popcount64_slow(uint64 word);
+extern uint64 pg_popcount_slow(const char *buf, int bytes);
+extern uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
+
 #ifdef TRY_POPCNT_X86_64
-/* Attempt to use the POPCNT instruction, but perform a runtime check first */
+/*
+ * Attempt to use SSE4.2 or AVX-512 instructions, but perform a runtime check
+ * first.
+ */
 extern PGDLLIMPORT int (*pg_popcount32) (uint32 word);
 extern PGDLLIMPORT int (*pg_popcount64) (uint64 word);
 extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes);
 extern PGDLLIMPORT uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask);
 
-/*
- * We can also try to use the AVX-512 popcount instruction on some systems.
- * The implementation of that is located in its own file.
- */
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-extern bool pg_popcount_avx512_available(void);
-extern uint64 pg_popcount_avx512(const char *buf, int bytes);
-extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
-#endif
-
 #elif POPCNT_AARCH64
 /* Use the Neon version of pg_popcount{32,64} without function pointer. */
 extern int	pg_popcount32(uint32 word);
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 7875bbb0f4b..9f9f90ddd4d 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -12,13 +12,6 @@
  */
 #include "c.h"
 
-#ifdef HAVE__GET_CPUID
-#include <cpuid.h>
-#endif
-#ifdef HAVE__CPUID
-#include <intrin.h>
-#endif
-
 #include "port/pg_bitutils.h"
 
 
@@ -103,257 +96,11 @@ const uint8 pg_number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-static inline int pg_popcount32_slow(uint32 word);
-static inline int pg_popcount64_slow(uint64 word);
-static uint64 pg_popcount_slow(const char *buf, int bytes);
-static uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
-#endif
-
-#ifdef TRY_POPCNT_X86_64
-static bool pg_popcount_available(void);
-static int	pg_popcount32_choose(uint32 word);
-static int	pg_popcount64_choose(uint64 word);
-static uint64 pg_popcount_choose(const char *buf, int bytes);
-static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
-static inline int pg_popcount32_fast(uint32 word);
-static inline int pg_popcount64_fast(uint64 word);
-static uint64 pg_popcount_fast(const char *buf, int bytes);
-static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
-
-int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
-int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
-uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
-uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
-#endif							/* TRY_POPCNT_X86_64 */
-
-#ifdef TRY_POPCNT_X86_64
-
-/*
- * Return true if CPUID indicates that the POPCNT instruction is available.
- */
-static bool
-pg_popcount_available(void)
-{
-	unsigned int exx[4] = {0, 0, 0, 0};
-
-#if defined(HAVE__GET_CPUID)
-	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
-#elif defined(HAVE__CPUID)
-	__cpuid(exx, 1);
-#else
-#error cpuid instruction not available
-#endif
-
-	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
-}
-
-/*
- * These functions get called on the first call to pg_popcount32 etc.
- * They detect whether we can use the asm implementations, and replace
- * the function pointers so that subsequent calls are routed directly to
- * the chosen implementation.
- */
-static inline void
-choose_popcount_functions(void)
-{
-	if (pg_popcount_available())
-	{
-		pg_popcount32 = pg_popcount32_fast;
-		pg_popcount64 = pg_popcount64_fast;
-		pg_popcount_optimized = pg_popcount_fast;
-		pg_popcount_masked_optimized = pg_popcount_masked_fast;
-	}
-	else
-	{
-		pg_popcount32 = pg_popcount32_slow;
-		pg_popcount64 = pg_popcount64_slow;
-		pg_popcount_optimized = pg_popcount_slow;
-		pg_popcount_masked_optimized = pg_popcount_masked_slow;
-	}
-
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-	if (pg_popcount_avx512_available())
-	{
-		pg_popcount_optimized = pg_popcount_avx512;
-		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
-	}
-#endif
-}
-
-static int
-pg_popcount32_choose(uint32 word)
-{
-	choose_popcount_functions();
-	return pg_popcount32(word);
-}
-
-static int
-pg_popcount64_choose(uint64 word)
-{
-	choose_popcount_functions();
-	return pg_popcount64(word);
-}
-
-static uint64
-pg_popcount_choose(const char *buf, int bytes)
-{
-	choose_popcount_functions();
-	return pg_popcount_optimized(buf, bytes);
-}
-
-static uint64
-pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
-{
-	choose_popcount_functions();
-	return pg_popcount_masked(buf, bytes, mask);
-}
-
-/*
- * pg_popcount32_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount32_fast(uint32 word)
-{
-#ifdef _MSC_VER
-	return __popcnt(word);
-#else
-	uint32		res;
-
-__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount64_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount64_fast(uint64 word)
-{
-#ifdef _MSC_VER
-	return __popcnt64(word);
-#else
-	uint64		res;
-
-__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount_fast
- *		Returns the number of 1-bits in buf
- */
-static uint64
-pg_popcount_fast(const char *buf, int bytes)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++];
-
-	return popcnt;
-}
-
-/*
- * pg_popcount_masked_fast
- *		Returns the number of 1-bits in buf after applying the mask to each byte
- */
-static uint64
-pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned */
-	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++ & maskv);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++ & maskv);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
-
-	return popcnt;
-}
-
-#endif							/* TRY_POPCNT_X86_64 */
-
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-
 /*
  * pg_popcount32_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount32_slow(uint32 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -375,7 +122,7 @@ pg_popcount32_slow(uint32 word)
  * pg_popcount64_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -403,7 +150,7 @@ pg_popcount64_slow(uint64 word)
  * pg_popcount_slow
  *		Returns the number of 1-bits in buf
  */
-static uint64
+uint64
 pg_popcount_slow(const char *buf, int bytes)
 {
 	uint64		popcnt = 0;
@@ -449,7 +196,7 @@ pg_popcount_slow(const char *buf, int bytes)
  * pg_popcount_masked_slow
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
-static uint64
+uint64
 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 {
 	uint64		popcnt = 0;
@@ -495,8 +242,6 @@ pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 	return popcnt;
 }
 
-#endif							/* ! POPCNT_AARCH64 */
-
 #if !defined(TRY_POPCNT_X86_64) && !defined(POPCNT_AARCH64)
 
 /*
diff --git a/src/port/pg_popcount_x86_64.c b/src/port/pg_popcount_x86_64.c
index 453c7a06ce9..f8643642613 100644
--- a/src/port/pg_popcount_x86_64.c
+++ b/src/port/pg_popcount_x86_64.c
@@ -12,26 +12,74 @@
  */
 #include "c.h"
 
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+#include "port/pg_bitutils.h"
+
+#ifdef TRY_POPCNT_X86_64
 
 #if defined(HAVE__GET_CPUID) || defined(HAVE__GET_CPUID_COUNT)
 #include <cpuid.h>
 #endif
 
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 #include <immintrin.h>
+#endif
 
 #if defined(HAVE__CPUID) || defined(HAVE__CPUIDEX)
 #include <intrin.h>
 #endif
 
-#include "port/pg_bitutils.h"
+/*
+ * The SSE4.2 versions are built regardless of whether we are building the
+ * AVX-512 versions.
+ */
+static inline int pg_popcount32_fast(uint32 word);
+static inline int pg_popcount64_fast(uint64 word);
+static uint64 pg_popcount_fast(const char *buf, int bytes);
+static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
 
 /*
- * It's probably unlikely that TRY_POPCNT_X86_64 won't be set if we are able to
- * use AVX-512 intrinsics, but we check it anyway to be sure.  We piggy-back on
- * the function pointers that are only used when TRY_POPCNT_X86_64 is set.
+ * These are the AVX-512 implementations of the popcount functions.
  */
-#ifdef TRY_POPCNT_X86_64
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+static uint64 pg_popcount_avx512(const char *buf, int bytes);
+static uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * The function pointers are initially set to "choose" functions.  These
+ * functions will first set the pointers to the right implementations (base on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+static int	pg_popcount32_choose(uint32 word);
+static int	pg_popcount64_choose(uint64 word);
+static uint64 pg_popcount_choose(const char *buf, int bytes);
+static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
+int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
+int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
+uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
+uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
+
+/*
+ * Return true if CPUID indicates that the POPCNT instruction is available.
+ */
+static bool
+pg_popcount_available(void)
+{
+	unsigned int exx[4] = {0, 0, 0, 0};
+
+#if defined(HAVE__GET_CPUID)
+	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
+#elif defined(HAVE__CPUID)
+	__cpuid(exx, 1);
+#else
+#error cpuid instruction not available
+#endif
+
+	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 
 /*
  * Does CPUID say there's support for XSAVE instructions?
@@ -94,7 +142,7 @@ avx512_popcnt_available(void)
  * Returns true if the CPU supports the instructions required for the AVX-512
  * pg_popcount() implementation.
  */
-bool
+static bool
 pg_popcount_avx512_available(void)
 {
 	return xsave_available() &&
@@ -102,12 +150,77 @@ pg_popcount_avx512_available(void)
 		avx512_popcnt_available();
 }
 
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * These functions get called on the first call to pg_popcount32 etc.
+ * They detect whether we can use the asm implementations, and replace
+ * the function pointers so that subsequent calls are routed directly to
+ * the chosen implementation.
+ */
+static inline void
+choose_popcount_functions(void)
+{
+	if (pg_popcount_available())
+	{
+		pg_popcount32 = pg_popcount32_fast;
+		pg_popcount64 = pg_popcount64_fast;
+		pg_popcount_optimized = pg_popcount_fast;
+		pg_popcount_masked_optimized = pg_popcount_masked_fast;
+	}
+	else
+	{
+		pg_popcount32 = pg_popcount32_slow;
+		pg_popcount64 = pg_popcount64_slow;
+		pg_popcount_optimized = pg_popcount_slow;
+		pg_popcount_masked_optimized = pg_popcount_masked_slow;
+	}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+	if (pg_popcount_avx512_available())
+	{
+		pg_popcount_optimized = pg_popcount_avx512;
+		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
+	}
+#endif
+}
+
+static int
+pg_popcount32_choose(uint32 word)
+{
+	choose_popcount_functions();
+	return pg_popcount32(word);
+}
+
+static int
+pg_popcount64_choose(uint64 word)
+{
+	choose_popcount_functions();
+	return pg_popcount64(word);
+}
+
+static uint64
+pg_popcount_choose(const char *buf, int bytes)
+{
+	choose_popcount_functions();
+	return pg_popcount_optimized(buf, bytes);
+}
+
+static uint64
+pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
+{
+	choose_popcount_functions();
+	return pg_popcount_masked(buf, bytes, mask);
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+
 /*
  * pg_popcount_avx512
  *		Returns the number of 1-bits in buf
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_avx512(const char *buf, int bytes)
 {
 	__m512i		val,
@@ -163,7 +276,7 @@ pg_popcount_avx512(const char *buf, int bytes)
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 {
 	__m512i		val,
@@ -219,5 +332,136 @@ pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 	return _mm512_reduce_add_epi64(accum);
 }
 
-#endif							/* TRY_POPCNT_X86_64 */
 #endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * pg_popcount32_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount32_fast(uint32 word)
+{
+#ifdef _MSC_VER
+	return __popcnt(word);
+#else
+	uint32		res;
+
+__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount64_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount64_fast(uint64 word)
+{
+#ifdef _MSC_VER
+	return __popcnt64(word);
+#else
+	uint64		res;
+
+__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount_fast
+ *		Returns the number of 1-bits in buf
+ */
+static uint64
+pg_popcount_fast(const char *buf, int bytes)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++];
+
+	return popcnt;
+}
+
+/*
+ * pg_popcount_masked_fast
+ *		Returns the number of 1-bits in buf after applying the mask to each byte
+ */
+static uint64
+pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned */
+	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++ & maskv);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++ & maskv);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
+
+	return popcnt;
+}
+
+#endif							/* TRY_POPCNT_X86_64 */
-- 
2.50.1 (Apple Git-155)


--RzZ1MyDExwO9gtVK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0003-Rename-fast-and-slow-popcount-functions.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] Move x86 popcount code to pg_popcount_x86_64.c.
@ 2026-01-14 17:54 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-01-14 17:54 UTC (permalink / raw)

This moves the SSE4.2 popcount implementations to the recently
renamed file for x86-64-specific popcount code.
---
 src/include/port/pg_bitutils.h |  20 ++-
 src/port/pg_bitutils.c         | 263 +-------------------------------
 src/port/pg_popcount_x86.c     | 264 +++++++++++++++++++++++++++++++--
 3 files changed, 267 insertions(+), 280 deletions(-)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 8ed12f7a021..c9c508d4ba3 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -307,23 +307,21 @@ pg_ceil_log2_64(uint64 num)
 #define POPCNT_AARCH64 1
 #endif
 
+extern int	pg_popcount32_slow(uint32 word);
+extern int	pg_popcount64_slow(uint64 word);
+extern uint64 pg_popcount_slow(const char *buf, int bytes);
+extern uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
+
 #ifdef TRY_POPCNT_X86_64
-/* Attempt to use the POPCNT instruction, but perform a runtime check first */
+/*
+ * Attempt to use SSE4.2 or AVX-512 instructions, but perform a runtime check
+ * first.
+ */
 extern PGDLLIMPORT int (*pg_popcount32) (uint32 word);
 extern PGDLLIMPORT int (*pg_popcount64) (uint64 word);
 extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes);
 extern PGDLLIMPORT uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask);
 
-/*
- * We can also try to use the AVX-512 popcount instruction on some systems.
- * The implementation of that is located in its own file.
- */
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-extern bool pg_popcount_avx512_available(void);
-extern uint64 pg_popcount_avx512(const char *buf, int bytes);
-extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
-#endif
-
 #elif POPCNT_AARCH64
 /* Use the Neon version of pg_popcount{32,64} without function pointer. */
 extern int	pg_popcount32(uint32 word);
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 7875bbb0f4b..9f9f90ddd4d 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -12,13 +12,6 @@
  */
 #include "c.h"
 
-#ifdef HAVE__GET_CPUID
-#include <cpuid.h>
-#endif
-#ifdef HAVE__CPUID
-#include <intrin.h>
-#endif
-
 #include "port/pg_bitutils.h"
 
 
@@ -103,257 +96,11 @@ const uint8 pg_number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-static inline int pg_popcount32_slow(uint32 word);
-static inline int pg_popcount64_slow(uint64 word);
-static uint64 pg_popcount_slow(const char *buf, int bytes);
-static uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
-#endif
-
-#ifdef TRY_POPCNT_X86_64
-static bool pg_popcount_available(void);
-static int	pg_popcount32_choose(uint32 word);
-static int	pg_popcount64_choose(uint64 word);
-static uint64 pg_popcount_choose(const char *buf, int bytes);
-static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
-static inline int pg_popcount32_fast(uint32 word);
-static inline int pg_popcount64_fast(uint64 word);
-static uint64 pg_popcount_fast(const char *buf, int bytes);
-static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
-
-int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
-int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
-uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
-uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
-#endif							/* TRY_POPCNT_X86_64 */
-
-#ifdef TRY_POPCNT_X86_64
-
-/*
- * Return true if CPUID indicates that the POPCNT instruction is available.
- */
-static bool
-pg_popcount_available(void)
-{
-	unsigned int exx[4] = {0, 0, 0, 0};
-
-#if defined(HAVE__GET_CPUID)
-	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
-#elif defined(HAVE__CPUID)
-	__cpuid(exx, 1);
-#else
-#error cpuid instruction not available
-#endif
-
-	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
-}
-
-/*
- * These functions get called on the first call to pg_popcount32 etc.
- * They detect whether we can use the asm implementations, and replace
- * the function pointers so that subsequent calls are routed directly to
- * the chosen implementation.
- */
-static inline void
-choose_popcount_functions(void)
-{
-	if (pg_popcount_available())
-	{
-		pg_popcount32 = pg_popcount32_fast;
-		pg_popcount64 = pg_popcount64_fast;
-		pg_popcount_optimized = pg_popcount_fast;
-		pg_popcount_masked_optimized = pg_popcount_masked_fast;
-	}
-	else
-	{
-		pg_popcount32 = pg_popcount32_slow;
-		pg_popcount64 = pg_popcount64_slow;
-		pg_popcount_optimized = pg_popcount_slow;
-		pg_popcount_masked_optimized = pg_popcount_masked_slow;
-	}
-
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-	if (pg_popcount_avx512_available())
-	{
-		pg_popcount_optimized = pg_popcount_avx512;
-		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
-	}
-#endif
-}
-
-static int
-pg_popcount32_choose(uint32 word)
-{
-	choose_popcount_functions();
-	return pg_popcount32(word);
-}
-
-static int
-pg_popcount64_choose(uint64 word)
-{
-	choose_popcount_functions();
-	return pg_popcount64(word);
-}
-
-static uint64
-pg_popcount_choose(const char *buf, int bytes)
-{
-	choose_popcount_functions();
-	return pg_popcount_optimized(buf, bytes);
-}
-
-static uint64
-pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
-{
-	choose_popcount_functions();
-	return pg_popcount_masked(buf, bytes, mask);
-}
-
-/*
- * pg_popcount32_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount32_fast(uint32 word)
-{
-#ifdef _MSC_VER
-	return __popcnt(word);
-#else
-	uint32		res;
-
-__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount64_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount64_fast(uint64 word)
-{
-#ifdef _MSC_VER
-	return __popcnt64(word);
-#else
-	uint64		res;
-
-__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount_fast
- *		Returns the number of 1-bits in buf
- */
-static uint64
-pg_popcount_fast(const char *buf, int bytes)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++];
-
-	return popcnt;
-}
-
-/*
- * pg_popcount_masked_fast
- *		Returns the number of 1-bits in buf after applying the mask to each byte
- */
-static uint64
-pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned */
-	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++ & maskv);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++ & maskv);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
-
-	return popcnt;
-}
-
-#endif							/* TRY_POPCNT_X86_64 */
-
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-
 /*
  * pg_popcount32_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount32_slow(uint32 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -375,7 +122,7 @@ pg_popcount32_slow(uint32 word)
  * pg_popcount64_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -403,7 +150,7 @@ pg_popcount64_slow(uint64 word)
  * pg_popcount_slow
  *		Returns the number of 1-bits in buf
  */
-static uint64
+uint64
 pg_popcount_slow(const char *buf, int bytes)
 {
 	uint64		popcnt = 0;
@@ -449,7 +196,7 @@ pg_popcount_slow(const char *buf, int bytes)
  * pg_popcount_masked_slow
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
-static uint64
+uint64
 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 {
 	uint64		popcnt = 0;
@@ -495,8 +242,6 @@ pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 	return popcnt;
 }
 
-#endif							/* ! POPCNT_AARCH64 */
-
 #if !defined(TRY_POPCNT_X86_64) && !defined(POPCNT_AARCH64)
 
 /*
diff --git a/src/port/pg_popcount_x86.c b/src/port/pg_popcount_x86.c
index 453c7a06ce9..f8643642613 100644
--- a/src/port/pg_popcount_x86.c
+++ b/src/port/pg_popcount_x86.c
@@ -12,26 +12,74 @@
  */
 #include "c.h"
 
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+#include "port/pg_bitutils.h"
+
+#ifdef TRY_POPCNT_X86_64
 
 #if defined(HAVE__GET_CPUID) || defined(HAVE__GET_CPUID_COUNT)
 #include <cpuid.h>
 #endif
 
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 #include <immintrin.h>
+#endif
 
 #if defined(HAVE__CPUID) || defined(HAVE__CPUIDEX)
 #include <intrin.h>
 #endif
 
-#include "port/pg_bitutils.h"
+/*
+ * The SSE4.2 versions are built regardless of whether we are building the
+ * AVX-512 versions.
+ */
+static inline int pg_popcount32_fast(uint32 word);
+static inline int pg_popcount64_fast(uint64 word);
+static uint64 pg_popcount_fast(const char *buf, int bytes);
+static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
 
 /*
- * It's probably unlikely that TRY_POPCNT_X86_64 won't be set if we are able to
- * use AVX-512 intrinsics, but we check it anyway to be sure.  We piggy-back on
- * the function pointers that are only used when TRY_POPCNT_X86_64 is set.
+ * These are the AVX-512 implementations of the popcount functions.
  */
-#ifdef TRY_POPCNT_X86_64
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+static uint64 pg_popcount_avx512(const char *buf, int bytes);
+static uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * The function pointers are initially set to "choose" functions.  These
+ * functions will first set the pointers to the right implementations (base on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+static int	pg_popcount32_choose(uint32 word);
+static int	pg_popcount64_choose(uint64 word);
+static uint64 pg_popcount_choose(const char *buf, int bytes);
+static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
+int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
+int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
+uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
+uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
+
+/*
+ * Return true if CPUID indicates that the POPCNT instruction is available.
+ */
+static bool
+pg_popcount_available(void)
+{
+	unsigned int exx[4] = {0, 0, 0, 0};
+
+#if defined(HAVE__GET_CPUID)
+	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
+#elif defined(HAVE__CPUID)
+	__cpuid(exx, 1);
+#else
+#error cpuid instruction not available
+#endif
+
+	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 
 /*
  * Does CPUID say there's support for XSAVE instructions?
@@ -94,7 +142,7 @@ avx512_popcnt_available(void)
  * Returns true if the CPU supports the instructions required for the AVX-512
  * pg_popcount() implementation.
  */
-bool
+static bool
 pg_popcount_avx512_available(void)
 {
 	return xsave_available() &&
@@ -102,12 +150,77 @@ pg_popcount_avx512_available(void)
 		avx512_popcnt_available();
 }
 
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * These functions get called on the first call to pg_popcount32 etc.
+ * They detect whether we can use the asm implementations, and replace
+ * the function pointers so that subsequent calls are routed directly to
+ * the chosen implementation.
+ */
+static inline void
+choose_popcount_functions(void)
+{
+	if (pg_popcount_available())
+	{
+		pg_popcount32 = pg_popcount32_fast;
+		pg_popcount64 = pg_popcount64_fast;
+		pg_popcount_optimized = pg_popcount_fast;
+		pg_popcount_masked_optimized = pg_popcount_masked_fast;
+	}
+	else
+	{
+		pg_popcount32 = pg_popcount32_slow;
+		pg_popcount64 = pg_popcount64_slow;
+		pg_popcount_optimized = pg_popcount_slow;
+		pg_popcount_masked_optimized = pg_popcount_masked_slow;
+	}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+	if (pg_popcount_avx512_available())
+	{
+		pg_popcount_optimized = pg_popcount_avx512;
+		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
+	}
+#endif
+}
+
+static int
+pg_popcount32_choose(uint32 word)
+{
+	choose_popcount_functions();
+	return pg_popcount32(word);
+}
+
+static int
+pg_popcount64_choose(uint64 word)
+{
+	choose_popcount_functions();
+	return pg_popcount64(word);
+}
+
+static uint64
+pg_popcount_choose(const char *buf, int bytes)
+{
+	choose_popcount_functions();
+	return pg_popcount_optimized(buf, bytes);
+}
+
+static uint64
+pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
+{
+	choose_popcount_functions();
+	return pg_popcount_masked(buf, bytes, mask);
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+
 /*
  * pg_popcount_avx512
  *		Returns the number of 1-bits in buf
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_avx512(const char *buf, int bytes)
 {
 	__m512i		val,
@@ -163,7 +276,7 @@ pg_popcount_avx512(const char *buf, int bytes)
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 {
 	__m512i		val,
@@ -219,5 +332,136 @@ pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 	return _mm512_reduce_add_epi64(accum);
 }
 
-#endif							/* TRY_POPCNT_X86_64 */
 #endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * pg_popcount32_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount32_fast(uint32 word)
+{
+#ifdef _MSC_VER
+	return __popcnt(word);
+#else
+	uint32		res;
+
+__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount64_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount64_fast(uint64 word)
+{
+#ifdef _MSC_VER
+	return __popcnt64(word);
+#else
+	uint64		res;
+
+__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount_fast
+ *		Returns the number of 1-bits in buf
+ */
+static uint64
+pg_popcount_fast(const char *buf, int bytes)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++];
+
+	return popcnt;
+}
+
+/*
+ * pg_popcount_masked_fast
+ *		Returns the number of 1-bits in buf after applying the mask to each byte
+ */
+static uint64
+pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned */
+	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++ & maskv);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++ & maskv);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
+
+	return popcnt;
+}
+
+#endif							/* TRY_POPCNT_X86_64 */
-- 
2.50.1 (Apple Git-155)


--KuW0xlRTZoVkcm/J
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-Rename-fast-and-slow-popcount-functions.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] Move x86 popcount code to pg_popcount_x86_64.c.
@ 2026-01-14 17:54 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-01-14 17:54 UTC (permalink / raw)

This moves the SSE4.2 popcount implementations to the recently
renamed file for x86-64-specific popcount code.
---
 src/include/port/pg_bitutils.h |  20 ++-
 src/port/pg_bitutils.c         | 263 +-------------------------------
 src/port/pg_popcount_x86.c     | 264 +++++++++++++++++++++++++++++++--
 3 files changed, 267 insertions(+), 280 deletions(-)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 8ed12f7a021..c9c508d4ba3 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -307,23 +307,21 @@ pg_ceil_log2_64(uint64 num)
 #define POPCNT_AARCH64 1
 #endif
 
+extern int	pg_popcount32_slow(uint32 word);
+extern int	pg_popcount64_slow(uint64 word);
+extern uint64 pg_popcount_slow(const char *buf, int bytes);
+extern uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
+
 #ifdef TRY_POPCNT_X86_64
-/* Attempt to use the POPCNT instruction, but perform a runtime check first */
+/*
+ * Attempt to use SSE4.2 or AVX-512 instructions, but perform a runtime check
+ * first.
+ */
 extern PGDLLIMPORT int (*pg_popcount32) (uint32 word);
 extern PGDLLIMPORT int (*pg_popcount64) (uint64 word);
 extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes);
 extern PGDLLIMPORT uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask);
 
-/*
- * We can also try to use the AVX-512 popcount instruction on some systems.
- * The implementation of that is located in its own file.
- */
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-extern bool pg_popcount_avx512_available(void);
-extern uint64 pg_popcount_avx512(const char *buf, int bytes);
-extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
-#endif
-
 #elif POPCNT_AARCH64
 /* Use the Neon version of pg_popcount{32,64} without function pointer. */
 extern int	pg_popcount32(uint32 word);
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 7875bbb0f4b..9f9f90ddd4d 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -12,13 +12,6 @@
  */
 #include "c.h"
 
-#ifdef HAVE__GET_CPUID
-#include <cpuid.h>
-#endif
-#ifdef HAVE__CPUID
-#include <intrin.h>
-#endif
-
 #include "port/pg_bitutils.h"
 
 
@@ -103,257 +96,11 @@ const uint8 pg_number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-static inline int pg_popcount32_slow(uint32 word);
-static inline int pg_popcount64_slow(uint64 word);
-static uint64 pg_popcount_slow(const char *buf, int bytes);
-static uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
-#endif
-
-#ifdef TRY_POPCNT_X86_64
-static bool pg_popcount_available(void);
-static int	pg_popcount32_choose(uint32 word);
-static int	pg_popcount64_choose(uint64 word);
-static uint64 pg_popcount_choose(const char *buf, int bytes);
-static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
-static inline int pg_popcount32_fast(uint32 word);
-static inline int pg_popcount64_fast(uint64 word);
-static uint64 pg_popcount_fast(const char *buf, int bytes);
-static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
-
-int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
-int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
-uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
-uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
-#endif							/* TRY_POPCNT_X86_64 */
-
-#ifdef TRY_POPCNT_X86_64
-
-/*
- * Return true if CPUID indicates that the POPCNT instruction is available.
- */
-static bool
-pg_popcount_available(void)
-{
-	unsigned int exx[4] = {0, 0, 0, 0};
-
-#if defined(HAVE__GET_CPUID)
-	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
-#elif defined(HAVE__CPUID)
-	__cpuid(exx, 1);
-#else
-#error cpuid instruction not available
-#endif
-
-	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
-}
-
-/*
- * These functions get called on the first call to pg_popcount32 etc.
- * They detect whether we can use the asm implementations, and replace
- * the function pointers so that subsequent calls are routed directly to
- * the chosen implementation.
- */
-static inline void
-choose_popcount_functions(void)
-{
-	if (pg_popcount_available())
-	{
-		pg_popcount32 = pg_popcount32_fast;
-		pg_popcount64 = pg_popcount64_fast;
-		pg_popcount_optimized = pg_popcount_fast;
-		pg_popcount_masked_optimized = pg_popcount_masked_fast;
-	}
-	else
-	{
-		pg_popcount32 = pg_popcount32_slow;
-		pg_popcount64 = pg_popcount64_slow;
-		pg_popcount_optimized = pg_popcount_slow;
-		pg_popcount_masked_optimized = pg_popcount_masked_slow;
-	}
-
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-	if (pg_popcount_avx512_available())
-	{
-		pg_popcount_optimized = pg_popcount_avx512;
-		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
-	}
-#endif
-}
-
-static int
-pg_popcount32_choose(uint32 word)
-{
-	choose_popcount_functions();
-	return pg_popcount32(word);
-}
-
-static int
-pg_popcount64_choose(uint64 word)
-{
-	choose_popcount_functions();
-	return pg_popcount64(word);
-}
-
-static uint64
-pg_popcount_choose(const char *buf, int bytes)
-{
-	choose_popcount_functions();
-	return pg_popcount_optimized(buf, bytes);
-}
-
-static uint64
-pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
-{
-	choose_popcount_functions();
-	return pg_popcount_masked(buf, bytes, mask);
-}
-
-/*
- * pg_popcount32_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount32_fast(uint32 word)
-{
-#ifdef _MSC_VER
-	return __popcnt(word);
-#else
-	uint32		res;
-
-__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount64_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount64_fast(uint64 word)
-{
-#ifdef _MSC_VER
-	return __popcnt64(word);
-#else
-	uint64		res;
-
-__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount_fast
- *		Returns the number of 1-bits in buf
- */
-static uint64
-pg_popcount_fast(const char *buf, int bytes)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++];
-
-	return popcnt;
-}
-
-/*
- * pg_popcount_masked_fast
- *		Returns the number of 1-bits in buf after applying the mask to each byte
- */
-static uint64
-pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned */
-	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++ & maskv);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++ & maskv);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
-
-	return popcnt;
-}
-
-#endif							/* TRY_POPCNT_X86_64 */
-
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-
 /*
  * pg_popcount32_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount32_slow(uint32 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -375,7 +122,7 @@ pg_popcount32_slow(uint32 word)
  * pg_popcount64_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -403,7 +150,7 @@ pg_popcount64_slow(uint64 word)
  * pg_popcount_slow
  *		Returns the number of 1-bits in buf
  */
-static uint64
+uint64
 pg_popcount_slow(const char *buf, int bytes)
 {
 	uint64		popcnt = 0;
@@ -449,7 +196,7 @@ pg_popcount_slow(const char *buf, int bytes)
  * pg_popcount_masked_slow
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
-static uint64
+uint64
 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 {
 	uint64		popcnt = 0;
@@ -495,8 +242,6 @@ pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 	return popcnt;
 }
 
-#endif							/* ! POPCNT_AARCH64 */
-
 #if !defined(TRY_POPCNT_X86_64) && !defined(POPCNT_AARCH64)
 
 /*
diff --git a/src/port/pg_popcount_x86.c b/src/port/pg_popcount_x86.c
index 453c7a06ce9..f8643642613 100644
--- a/src/port/pg_popcount_x86.c
+++ b/src/port/pg_popcount_x86.c
@@ -12,26 +12,74 @@
  */
 #include "c.h"
 
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+#include "port/pg_bitutils.h"
+
+#ifdef TRY_POPCNT_X86_64
 
 #if defined(HAVE__GET_CPUID) || defined(HAVE__GET_CPUID_COUNT)
 #include <cpuid.h>
 #endif
 
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 #include <immintrin.h>
+#endif
 
 #if defined(HAVE__CPUID) || defined(HAVE__CPUIDEX)
 #include <intrin.h>
 #endif
 
-#include "port/pg_bitutils.h"
+/*
+ * The SSE4.2 versions are built regardless of whether we are building the
+ * AVX-512 versions.
+ */
+static inline int pg_popcount32_fast(uint32 word);
+static inline int pg_popcount64_fast(uint64 word);
+static uint64 pg_popcount_fast(const char *buf, int bytes);
+static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
 
 /*
- * It's probably unlikely that TRY_POPCNT_X86_64 won't be set if we are able to
- * use AVX-512 intrinsics, but we check it anyway to be sure.  We piggy-back on
- * the function pointers that are only used when TRY_POPCNT_X86_64 is set.
+ * These are the AVX-512 implementations of the popcount functions.
  */
-#ifdef TRY_POPCNT_X86_64
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+static uint64 pg_popcount_avx512(const char *buf, int bytes);
+static uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * The function pointers are initially set to "choose" functions.  These
+ * functions will first set the pointers to the right implementations (base on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+static int	pg_popcount32_choose(uint32 word);
+static int	pg_popcount64_choose(uint64 word);
+static uint64 pg_popcount_choose(const char *buf, int bytes);
+static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
+int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
+int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
+uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
+uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
+
+/*
+ * Return true if CPUID indicates that the POPCNT instruction is available.
+ */
+static bool
+pg_popcount_available(void)
+{
+	unsigned int exx[4] = {0, 0, 0, 0};
+
+#if defined(HAVE__GET_CPUID)
+	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
+#elif defined(HAVE__CPUID)
+	__cpuid(exx, 1);
+#else
+#error cpuid instruction not available
+#endif
+
+	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 
 /*
  * Does CPUID say there's support for XSAVE instructions?
@@ -94,7 +142,7 @@ avx512_popcnt_available(void)
  * Returns true if the CPU supports the instructions required for the AVX-512
  * pg_popcount() implementation.
  */
-bool
+static bool
 pg_popcount_avx512_available(void)
 {
 	return xsave_available() &&
@@ -102,12 +150,77 @@ pg_popcount_avx512_available(void)
 		avx512_popcnt_available();
 }
 
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * These functions get called on the first call to pg_popcount32 etc.
+ * They detect whether we can use the asm implementations, and replace
+ * the function pointers so that subsequent calls are routed directly to
+ * the chosen implementation.
+ */
+static inline void
+choose_popcount_functions(void)
+{
+	if (pg_popcount_available())
+	{
+		pg_popcount32 = pg_popcount32_fast;
+		pg_popcount64 = pg_popcount64_fast;
+		pg_popcount_optimized = pg_popcount_fast;
+		pg_popcount_masked_optimized = pg_popcount_masked_fast;
+	}
+	else
+	{
+		pg_popcount32 = pg_popcount32_slow;
+		pg_popcount64 = pg_popcount64_slow;
+		pg_popcount_optimized = pg_popcount_slow;
+		pg_popcount_masked_optimized = pg_popcount_masked_slow;
+	}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+	if (pg_popcount_avx512_available())
+	{
+		pg_popcount_optimized = pg_popcount_avx512;
+		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
+	}
+#endif
+}
+
+static int
+pg_popcount32_choose(uint32 word)
+{
+	choose_popcount_functions();
+	return pg_popcount32(word);
+}
+
+static int
+pg_popcount64_choose(uint64 word)
+{
+	choose_popcount_functions();
+	return pg_popcount64(word);
+}
+
+static uint64
+pg_popcount_choose(const char *buf, int bytes)
+{
+	choose_popcount_functions();
+	return pg_popcount_optimized(buf, bytes);
+}
+
+static uint64
+pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
+{
+	choose_popcount_functions();
+	return pg_popcount_masked(buf, bytes, mask);
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+
 /*
  * pg_popcount_avx512
  *		Returns the number of 1-bits in buf
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_avx512(const char *buf, int bytes)
 {
 	__m512i		val,
@@ -163,7 +276,7 @@ pg_popcount_avx512(const char *buf, int bytes)
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 {
 	__m512i		val,
@@ -219,5 +332,136 @@ pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 	return _mm512_reduce_add_epi64(accum);
 }
 
-#endif							/* TRY_POPCNT_X86_64 */
 #endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * pg_popcount32_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount32_fast(uint32 word)
+{
+#ifdef _MSC_VER
+	return __popcnt(word);
+#else
+	uint32		res;
+
+__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount64_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount64_fast(uint64 word)
+{
+#ifdef _MSC_VER
+	return __popcnt64(word);
+#else
+	uint64		res;
+
+__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount_fast
+ *		Returns the number of 1-bits in buf
+ */
+static uint64
+pg_popcount_fast(const char *buf, int bytes)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++];
+
+	return popcnt;
+}
+
+/*
+ * pg_popcount_masked_fast
+ *		Returns the number of 1-bits in buf after applying the mask to each byte
+ */
+static uint64
+pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned */
+	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++ & maskv);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++ & maskv);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
+
+	return popcnt;
+}
+
+#endif							/* TRY_POPCNT_X86_64 */
-- 
2.50.1 (Apple Git-155)


--KuW0xlRTZoVkcm/J
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-Rename-fast-and-slow-popcount-functions.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v1 2/3] Move x86 popcount code to pg_popcount_x86_64.c.
@ 2026-01-14 17:54 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-01-14 17:54 UTC (permalink / raw)

This moves the SSE4.2 popcount implementations to the recently
renamed file for x86-64-specific popcount code.
---
 src/include/port/pg_bitutils.h |  20 ++-
 src/port/pg_bitutils.c         | 263 +-------------------------------
 src/port/pg_popcount_x86_64.c  | 264 +++++++++++++++++++++++++++++++--
 3 files changed, 267 insertions(+), 280 deletions(-)

diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h
index 8ed12f7a021..c9c508d4ba3 100644
--- a/src/include/port/pg_bitutils.h
+++ b/src/include/port/pg_bitutils.h
@@ -307,23 +307,21 @@ pg_ceil_log2_64(uint64 num)
 #define POPCNT_AARCH64 1
 #endif
 
+extern int	pg_popcount32_slow(uint32 word);
+extern int	pg_popcount64_slow(uint64 word);
+extern uint64 pg_popcount_slow(const char *buf, int bytes);
+extern uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
+
 #ifdef TRY_POPCNT_X86_64
-/* Attempt to use the POPCNT instruction, but perform a runtime check first */
+/*
+ * Attempt to use SSE4.2 or AVX-512 instructions, but perform a runtime check
+ * first.
+ */
 extern PGDLLIMPORT int (*pg_popcount32) (uint32 word);
 extern PGDLLIMPORT int (*pg_popcount64) (uint64 word);
 extern PGDLLIMPORT uint64 (*pg_popcount_optimized) (const char *buf, int bytes);
 extern PGDLLIMPORT uint64 (*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask);
 
-/*
- * We can also try to use the AVX-512 popcount instruction on some systems.
- * The implementation of that is located in its own file.
- */
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-extern bool pg_popcount_avx512_available(void);
-extern uint64 pg_popcount_avx512(const char *buf, int bytes);
-extern uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
-#endif
-
 #elif POPCNT_AARCH64
 /* Use the Neon version of pg_popcount{32,64} without function pointer. */
 extern int	pg_popcount32(uint32 word);
diff --git a/src/port/pg_bitutils.c b/src/port/pg_bitutils.c
index 7875bbb0f4b..9f9f90ddd4d 100644
--- a/src/port/pg_bitutils.c
+++ b/src/port/pg_bitutils.c
@@ -12,13 +12,6 @@
  */
 #include "c.h"
 
-#ifdef HAVE__GET_CPUID
-#include <cpuid.h>
-#endif
-#ifdef HAVE__CPUID
-#include <intrin.h>
-#endif
-
 #include "port/pg_bitutils.h"
 
 
@@ -103,257 +96,11 @@ const uint8 pg_number_of_ones[256] = {
 	4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
 };
 
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-static inline int pg_popcount32_slow(uint32 word);
-static inline int pg_popcount64_slow(uint64 word);
-static uint64 pg_popcount_slow(const char *buf, int bytes);
-static uint64 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask);
-#endif
-
-#ifdef TRY_POPCNT_X86_64
-static bool pg_popcount_available(void);
-static int	pg_popcount32_choose(uint32 word);
-static int	pg_popcount64_choose(uint64 word);
-static uint64 pg_popcount_choose(const char *buf, int bytes);
-static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
-static inline int pg_popcount32_fast(uint32 word);
-static inline int pg_popcount64_fast(uint64 word);
-static uint64 pg_popcount_fast(const char *buf, int bytes);
-static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
-
-int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
-int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
-uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
-uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
-#endif							/* TRY_POPCNT_X86_64 */
-
-#ifdef TRY_POPCNT_X86_64
-
-/*
- * Return true if CPUID indicates that the POPCNT instruction is available.
- */
-static bool
-pg_popcount_available(void)
-{
-	unsigned int exx[4] = {0, 0, 0, 0};
-
-#if defined(HAVE__GET_CPUID)
-	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
-#elif defined(HAVE__CPUID)
-	__cpuid(exx, 1);
-#else
-#error cpuid instruction not available
-#endif
-
-	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
-}
-
-/*
- * These functions get called on the first call to pg_popcount32 etc.
- * They detect whether we can use the asm implementations, and replace
- * the function pointers so that subsequent calls are routed directly to
- * the chosen implementation.
- */
-static inline void
-choose_popcount_functions(void)
-{
-	if (pg_popcount_available())
-	{
-		pg_popcount32 = pg_popcount32_fast;
-		pg_popcount64 = pg_popcount64_fast;
-		pg_popcount_optimized = pg_popcount_fast;
-		pg_popcount_masked_optimized = pg_popcount_masked_fast;
-	}
-	else
-	{
-		pg_popcount32 = pg_popcount32_slow;
-		pg_popcount64 = pg_popcount64_slow;
-		pg_popcount_optimized = pg_popcount_slow;
-		pg_popcount_masked_optimized = pg_popcount_masked_slow;
-	}
-
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
-	if (pg_popcount_avx512_available())
-	{
-		pg_popcount_optimized = pg_popcount_avx512;
-		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
-	}
-#endif
-}
-
-static int
-pg_popcount32_choose(uint32 word)
-{
-	choose_popcount_functions();
-	return pg_popcount32(word);
-}
-
-static int
-pg_popcount64_choose(uint64 word)
-{
-	choose_popcount_functions();
-	return pg_popcount64(word);
-}
-
-static uint64
-pg_popcount_choose(const char *buf, int bytes)
-{
-	choose_popcount_functions();
-	return pg_popcount_optimized(buf, bytes);
-}
-
-static uint64
-pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
-{
-	choose_popcount_functions();
-	return pg_popcount_masked(buf, bytes, mask);
-}
-
-/*
- * pg_popcount32_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount32_fast(uint32 word)
-{
-#ifdef _MSC_VER
-	return __popcnt(word);
-#else
-	uint32		res;
-
-__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount64_fast
- *		Return the number of 1 bits set in word
- */
-static inline int
-pg_popcount64_fast(uint64 word)
-{
-#ifdef _MSC_VER
-	return __popcnt64(word);
-#else
-	uint64		res;
-
-__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
-	return (int) res;
-#endif
-}
-
-/*
- * pg_popcount_fast
- *		Returns the number of 1-bits in buf
- */
-static uint64
-pg_popcount_fast(const char *buf, int bytes)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++];
-
-	return popcnt;
-}
-
-/*
- * pg_popcount_masked_fast
- *		Returns the number of 1-bits in buf after applying the mask to each byte
- */
-static uint64
-pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
-{
-	uint64		popcnt = 0;
-
-#if SIZEOF_VOID_P >= 8
-	/* Process in 64-bit chunks if the buffer is aligned */
-	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(8, buf))
-	{
-		const uint64 *words = (const uint64 *) buf;
-
-		while (bytes >= 8)
-		{
-			popcnt += pg_popcount64_fast(*words++ & maskv);
-			bytes -= 8;
-		}
-
-		buf = (const char *) words;
-	}
-#else
-	/* Process in 32-bit chunks if the buffer is aligned. */
-	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
-
-	if (buf == (const char *) TYPEALIGN(4, buf))
-	{
-		const uint32 *words = (const uint32 *) buf;
-
-		while (bytes >= 4)
-		{
-			popcnt += pg_popcount32_fast(*words++ & maskv);
-			bytes -= 4;
-		}
-
-		buf = (const char *) words;
-	}
-#endif
-
-	/* Process any remaining bytes */
-	while (bytes--)
-		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
-
-	return popcnt;
-}
-
-#endif							/* TRY_POPCNT_X86_64 */
-
-/*
- * If we are building the Neon versions, we don't need the "slow" fallbacks.
- */
-#ifndef POPCNT_AARCH64
-
 /*
  * pg_popcount32_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount32_slow(uint32 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -375,7 +122,7 @@ pg_popcount32_slow(uint32 word)
  * pg_popcount64_slow
  *		Return the number of 1 bits set in word
  */
-static inline int
+int
 pg_popcount64_slow(uint64 word)
 {
 #ifdef HAVE__BUILTIN_POPCOUNT
@@ -403,7 +150,7 @@ pg_popcount64_slow(uint64 word)
  * pg_popcount_slow
  *		Returns the number of 1-bits in buf
  */
-static uint64
+uint64
 pg_popcount_slow(const char *buf, int bytes)
 {
 	uint64		popcnt = 0;
@@ -449,7 +196,7 @@ pg_popcount_slow(const char *buf, int bytes)
  * pg_popcount_masked_slow
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
-static uint64
+uint64
 pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 {
 	uint64		popcnt = 0;
@@ -495,8 +242,6 @@ pg_popcount_masked_slow(const char *buf, int bytes, bits8 mask)
 	return popcnt;
 }
 
-#endif							/* ! POPCNT_AARCH64 */
-
 #if !defined(TRY_POPCNT_X86_64) && !defined(POPCNT_AARCH64)
 
 /*
diff --git a/src/port/pg_popcount_x86_64.c b/src/port/pg_popcount_x86_64.c
index 453c7a06ce9..f8643642613 100644
--- a/src/port/pg_popcount_x86_64.c
+++ b/src/port/pg_popcount_x86_64.c
@@ -12,26 +12,74 @@
  */
 #include "c.h"
 
-#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+#include "port/pg_bitutils.h"
+
+#ifdef TRY_POPCNT_X86_64
 
 #if defined(HAVE__GET_CPUID) || defined(HAVE__GET_CPUID_COUNT)
 #include <cpuid.h>
 #endif
 
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 #include <immintrin.h>
+#endif
 
 #if defined(HAVE__CPUID) || defined(HAVE__CPUIDEX)
 #include <intrin.h>
 #endif
 
-#include "port/pg_bitutils.h"
+/*
+ * The SSE4.2 versions are built regardless of whether we are building the
+ * AVX-512 versions.
+ */
+static inline int pg_popcount32_fast(uint32 word);
+static inline int pg_popcount64_fast(uint64 word);
+static uint64 pg_popcount_fast(const char *buf, int bytes);
+static uint64 pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask);
 
 /*
- * It's probably unlikely that TRY_POPCNT_X86_64 won't be set if we are able to
- * use AVX-512 intrinsics, but we check it anyway to be sure.  We piggy-back on
- * the function pointers that are only used when TRY_POPCNT_X86_64 is set.
+ * These are the AVX-512 implementations of the popcount functions.
  */
-#ifdef TRY_POPCNT_X86_64
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+static uint64 pg_popcount_avx512(const char *buf, int bytes);
+static uint64 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask);
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * The function pointers are initially set to "choose" functions.  These
+ * functions will first set the pointers to the right implementations (base on
+ * what the current CPU supports) and then will call the pointer to fulfill the
+ * caller's request.
+ */
+static int	pg_popcount32_choose(uint32 word);
+static int	pg_popcount64_choose(uint64 word);
+static uint64 pg_popcount_choose(const char *buf, int bytes);
+static uint64 pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask);
+int			(*pg_popcount32) (uint32 word) = pg_popcount32_choose;
+int			(*pg_popcount64) (uint64 word) = pg_popcount64_choose;
+uint64		(*pg_popcount_optimized) (const char *buf, int bytes) = pg_popcount_choose;
+uint64		(*pg_popcount_masked_optimized) (const char *buf, int bytes, bits8 mask) = pg_popcount_masked_choose;
+
+/*
+ * Return true if CPUID indicates that the POPCNT instruction is available.
+ */
+static bool
+pg_popcount_available(void)
+{
+	unsigned int exx[4] = {0, 0, 0, 0};
+
+#if defined(HAVE__GET_CPUID)
+	__get_cpuid(1, &exx[0], &exx[1], &exx[2], &exx[3]);
+#elif defined(HAVE__CPUID)
+	__cpuid(exx, 1);
+#else
+#error cpuid instruction not available
+#endif
+
+	return (exx[2] & (1 << 23)) != 0;	/* POPCNT */
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
 
 /*
  * Does CPUID say there's support for XSAVE instructions?
@@ -94,7 +142,7 @@ avx512_popcnt_available(void)
  * Returns true if the CPU supports the instructions required for the AVX-512
  * pg_popcount() implementation.
  */
-bool
+static bool
 pg_popcount_avx512_available(void)
 {
 	return xsave_available() &&
@@ -102,12 +150,77 @@ pg_popcount_avx512_available(void)
 		avx512_popcnt_available();
 }
 
+#endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * These functions get called on the first call to pg_popcount32 etc.
+ * They detect whether we can use the asm implementations, and replace
+ * the function pointers so that subsequent calls are routed directly to
+ * the chosen implementation.
+ */
+static inline void
+choose_popcount_functions(void)
+{
+	if (pg_popcount_available())
+	{
+		pg_popcount32 = pg_popcount32_fast;
+		pg_popcount64 = pg_popcount64_fast;
+		pg_popcount_optimized = pg_popcount_fast;
+		pg_popcount_masked_optimized = pg_popcount_masked_fast;
+	}
+	else
+	{
+		pg_popcount32 = pg_popcount32_slow;
+		pg_popcount64 = pg_popcount64_slow;
+		pg_popcount_optimized = pg_popcount_slow;
+		pg_popcount_masked_optimized = pg_popcount_masked_slow;
+	}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+	if (pg_popcount_avx512_available())
+	{
+		pg_popcount_optimized = pg_popcount_avx512;
+		pg_popcount_masked_optimized = pg_popcount_masked_avx512;
+	}
+#endif
+}
+
+static int
+pg_popcount32_choose(uint32 word)
+{
+	choose_popcount_functions();
+	return pg_popcount32(word);
+}
+
+static int
+pg_popcount64_choose(uint64 word)
+{
+	choose_popcount_functions();
+	return pg_popcount64(word);
+}
+
+static uint64
+pg_popcount_choose(const char *buf, int bytes)
+{
+	choose_popcount_functions();
+	return pg_popcount_optimized(buf, bytes);
+}
+
+static uint64
+pg_popcount_masked_choose(const char *buf, int bytes, bits8 mask)
+{
+	choose_popcount_functions();
+	return pg_popcount_masked(buf, bytes, mask);
+}
+
+#ifdef USE_AVX512_POPCNT_WITH_RUNTIME_CHECK
+
 /*
  * pg_popcount_avx512
  *		Returns the number of 1-bits in buf
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_avx512(const char *buf, int bytes)
 {
 	__m512i		val,
@@ -163,7 +276,7 @@ pg_popcount_avx512(const char *buf, int bytes)
  *		Returns the number of 1-bits in buf after applying the mask to each byte
  */
 pg_attribute_target("avx512vpopcntdq,avx512bw")
-uint64
+static uint64
 pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 {
 	__m512i		val,
@@ -219,5 +332,136 @@ pg_popcount_masked_avx512(const char *buf, int bytes, bits8 mask)
 	return _mm512_reduce_add_epi64(accum);
 }
 
-#endif							/* TRY_POPCNT_X86_64 */
 #endif							/* USE_AVX512_POPCNT_WITH_RUNTIME_CHECK */
+
+/*
+ * pg_popcount32_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount32_fast(uint32 word)
+{
+#ifdef _MSC_VER
+	return __popcnt(word);
+#else
+	uint32		res;
+
+__asm__ __volatile__(" popcntl %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount64_fast
+ *		Return the number of 1 bits set in word
+ */
+static inline int
+pg_popcount64_fast(uint64 word)
+{
+#ifdef _MSC_VER
+	return __popcnt64(word);
+#else
+	uint64		res;
+
+__asm__ __volatile__(" popcntq %1,%0\n":"=q"(res):"rm"(word):"cc");
+	return (int) res;
+#endif
+}
+
+/*
+ * pg_popcount_fast
+ *		Returns the number of 1-bits in buf
+ */
+static uint64
+pg_popcount_fast(const char *buf, int bytes)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++];
+
+	return popcnt;
+}
+
+/*
+ * pg_popcount_masked_fast
+ *		Returns the number of 1-bits in buf after applying the mask to each byte
+ */
+static uint64
+pg_popcount_masked_fast(const char *buf, int bytes, bits8 mask)
+{
+	uint64		popcnt = 0;
+
+#if SIZEOF_VOID_P >= 8
+	/* Process in 64-bit chunks if the buffer is aligned */
+	uint64		maskv = ~UINT64CONST(0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(8, buf))
+	{
+		const uint64 *words = (const uint64 *) buf;
+
+		while (bytes >= 8)
+		{
+			popcnt += pg_popcount64_fast(*words++ & maskv);
+			bytes -= 8;
+		}
+
+		buf = (const char *) words;
+	}
+#else
+	/* Process in 32-bit chunks if the buffer is aligned. */
+	uint32		maskv = ~((uint32) 0) / 0xFF * mask;
+
+	if (buf == (const char *) TYPEALIGN(4, buf))
+	{
+		const uint32 *words = (const uint32 *) buf;
+
+		while (bytes >= 4)
+		{
+			popcnt += pg_popcount32_fast(*words++ & maskv);
+			bytes -= 4;
+		}
+
+		buf = (const char *) words;
+	}
+#endif
+
+	/* Process any remaining bytes */
+	while (bytes--)
+		popcnt += pg_number_of_ones[(unsigned char) *buf++ & mask];
+
+	return popcnt;
+}
+
+#endif							/* TRY_POPCNT_X86_64 */
-- 
2.50.1 (Apple Git-155)


--RzZ1MyDExwO9gtVK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v1-0003-Rename-fast-and-slow-popcount-functions.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--CT89ko5pLUrsvFtH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v2-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..01b354e3c44 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--ZcI1PtyEAe3VcVeQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v5-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml        |   2 +-
 src/bin/pg_upgrade/check.c             | 157 +----------------------
 src/bin/pg_upgrade/controldata.c       |  12 +-
 src/bin/pg_upgrade/exec.c              |  23 ----
 src/bin/pg_upgrade/file.c              | 164 -------------------------
 src/bin/pg_upgrade/multixact_rewrite.c |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c        |  31 +----
 src/bin/pg_upgrade/pg_upgrade.h        |  29 -----
 src/bin/pg_upgrade/relfilenumber.c     |  37 +-----
 src/bin/pg_upgrade/version.c           | 127 -------------------
 10 files changed, 16 insertions(+), 577 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 5a7afe62eab..0813cef2729 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index e1de61f36ee..479557abdcc 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1d767bbda2d..5a0d2045be5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--6vzYk1swa63ey9i6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v3-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)

---
 doc/src/sgml/ref/pgupgrade.sgml            |   2 +-
 src/bin/pg_upgrade/check.c                 | 157 +-------------------
 src/bin/pg_upgrade/controldata.c           |  98 +-----------
 src/bin/pg_upgrade/exec.c                  |  23 ---
 src/bin/pg_upgrade/file.c                  | 164 ---------------------
 src/bin/pg_upgrade/multixact_rewrite.c     |  11 +-
 src/bin/pg_upgrade/pg_upgrade.c            |  56 +------
 src/bin/pg_upgrade/pg_upgrade.h            |  29 ----
 src/bin/pg_upgrade/relfilenumber.c         |  37 +----
 src/bin/pg_upgrade/server.c                |   3 +-
 src/bin/pg_upgrade/t/006_transfer_modes.pl |  20 ---
 src/bin/pg_upgrade/version.c               | 127 ----------------
 12 files changed, 26 insertions(+), 701 deletions(-)

diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index cc44983a9a8..e4e8c02e6d6 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
  </para>
 
   <para>
-   <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+   <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
    major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
   </para>
 
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f8f31382835..786c1474829 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
 static void check_for_tables_with_oids(ClusterInfo *cluster);
 static void check_for_not_null_inheritance(ClusterInfo *cluster);
 static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
 static void check_for_new_tablespace_dir(void);
 static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
 static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = ALL_VERSIONS
 	},
 
-	/*
-	 * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
-	 * previously returned "not enabled" by default and was only functionally
-	 * enabled with a compile-time switch; as of 9.4 "line" has a different
-	 * on-disk representation format.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"line\" data type"),
-		.report_filename = "tables_using_line.txt",
-		.base_query =
-		"SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
-					 "This data type changed its internal and input/output format\n"
-					 "between your old and new versions so this\n"
-					 "cluster cannot currently be upgraded.  You can\n"
-					 "drop the problem columns and restart the upgrade.\n"),
-		.threshold_version = 903
-	},
-
 	/*
 	 * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
 	 * pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1500
 	},
 
-	/*
-	 * It's no longer allowed to create tables or views with "unknown"-type
-	 * columns.  We do not complain about views with such columns, because
-	 * they should get silently converted to "text" columns during the DDL
-	 * dump and reload; it seems unlikely to be worth making users do that by
-	 * hand.  However, if there's a table with such a column, the DDL reload
-	 * will fail, so we should pre-detect that rather than failing
-	 * mid-upgrade.  Worse, if there's a matview with such a column, the DDL
-	 * reload will silently change it to "text" which won't match the on-disk
-	 * storage (which is like "cstring").  So we *must* reject that.
-	 */
-	{
-		.status = gettext_noop("Checking for invalid \"unknown\" user columns"),
-		.report_filename = "tables_using_unknown.txt",
-		.base_query =
-		"SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
-					 "This data type is no longer allowed in tables, so this cluster\n"
-					 "cannot currently be upgraded.  You can drop the problem columns\n"
-					 "and restart the upgrade.\n"),
-		.threshold_version = 906
-	},
-
 	/*
 	 * PG 12 changed the 'sql_identifier' type storage to be based on name,
 	 * not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
 		.threshold_version = 1100
 	},
 
-	/*
-	 * JSONB changed its storage format during 9.4 beta, so check for it.
-	 */
-	{
-		.status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
-		.report_filename = "tables_using_jsonb.txt",
-		.base_query =
-		"SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
-		.report_text =
-		gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
-					 "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
-					 "cluster cannot currently be upgraded.  You can drop the problem \n"
-					 "columns and restart the upgrade.\n"),
-		.threshold_version = MANUAL_CHECK,
-		.version_hook = jsonb_9_4_check_applicable
-	},
-
 	/*
 	 * PG 12 removed types abstime, reltime, tinterval.
 	 */
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
 	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
 		check_for_gist_inet_ops(&old_cluster);
 
-	/*
-	 * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
-	 * hash indexes
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-	{
-		if (user_opts.check)
-			old_9_6_invalidate_hash_indexes(&old_cluster, true);
-	}
-
-	/* 9.5 and below should not have roles starting with pg_ */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
-		check_for_pg_role_prefix(&old_cluster);
-
 	/*
 	 * While not a check option, we do this now because this is the only time
 	 * the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
 			 * system boundaries.
 			 */
 			check_hard_link(TRANSFER_MODE_SWAP);
-
-			/*
-			 * There are a few known issues with using --swap to upgrade from
-			 * versions older than 10.  For example, the sequence tuple format
-			 * changed in v10, and the visibility map format changed in 9.6.
-			 * While such problems are not insurmountable (and we may have to
-			 * deal with similar problems in the future, anyway), it doesn't
-			 * seem worth the effort to support swap mode for upgrades from
-			 * long-unsupported versions.
-			 */
-			if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
-				pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
-						 "10");
-
 			break;
 	}
 
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
 	 */
 	start_postmaster(&new_cluster, true);
 
-	/* Reindex hash indexes for old < 10.0 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
-		old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
 	report_extension_updates(&new_cluster);
 
 	stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
 	 * upgrades
 	 */
 
-	if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+	if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
 		pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
-				 "9.2");
+				 "10");
 
 	/* Only current PG version is supported as a target */
 	if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
 						 ", 'array_cat(anyarray,anyarray)'"
 						 ", 'array_prepend(anyelement,anyarray)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_remove(anyarray,anyelement)'"
 							 ", 'array_replace(anyarray,anyelement,anyelement)'");
 
-	if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
 		appendPQExpBufferStr(&old_polymorphics,
 							 ", 'array_position(anyarray,anyelement)'"
 							 ", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
 		check_ok();
 }
 
-/*
- * check_for_pg_role_prefix()
- *
- *	Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
-	PGresult   *res;
-	PGconn	   *conn = connectToServer(cluster, "template1");
-	int			ntups;
-	int			i_roloid;
-	int			i_rolname;
-	FILE	   *script = NULL;
-	char		output_path[MAXPGPATH];
-
-	prep_status("Checking for roles starting with \"pg_\"");
-
-	snprintf(output_path, sizeof(output_path), "%s/%s",
-			 log_opts.basedir,
-			 "pg_role_prefix.txt");
-
-	res = executeQueryOrDie(conn,
-							"SELECT oid AS roloid, rolname "
-							"FROM pg_catalog.pg_roles "
-							"WHERE rolname ~ '^pg_'");
-
-	ntups = PQntuples(res);
-	i_roloid = PQfnumber(res, "roloid");
-	i_rolname = PQfnumber(res, "rolname");
-	for (int rowno = 0; rowno < ntups; rowno++)
-	{
-		if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-			pg_fatal("could not open file \"%s\": %m", output_path);
-		fprintf(script, "%s (oid=%s)\n",
-				PQgetvalue(res, rowno, i_rolname),
-				PQgetvalue(res, rowno, i_roloid));
-	}
-
-	PQclear(res);
-
-	PQfinish(conn);
-
-	if (script)
-	{
-		fclose(script);
-		pg_log(PG_REPORT, "fatal");
-		pg_fatal("Your installation contains roles starting with \"pg_\".\n"
-				 "\"pg_\" is a reserved prefix for system roles.  The cluster\n"
-				 "cannot be upgraded until these roles are renamed.\n"
-				 "A list of roles starting with \"pg_\" is in the file:\n"
-				 "    %s", output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * check_for_user_defined_encoding_conversions()'s UpgradeTask.  If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8f81c9e9582 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -42,9 +42,6 @@ get_control_data(ClusterInfo *cluster)
 	char		bufin[MAX_STRING];
 	FILE	   *output;
 	char	   *p;
-	bool		got_tli = false;
-	bool		got_log_id = false;
-	bool		got_log_seg = false;
 	bool		got_xid = false;
 	bool		got_oid = false;
 	bool		got_multi = false;
@@ -75,10 +72,6 @@ get_control_data(ClusterInfo *cluster)
 	char	   *language = NULL;
 	char	   *lc_all = NULL;
 	char	   *lc_messages = NULL;
-	uint32		tli = 0;
-	uint32		logid = 0;
-	uint32		segno = 0;
-	char	   *resetwal_bin;
 	int			rc;
 	bool		live_check = (cluster == &old_cluster && user_opts.live_check);
 
@@ -189,27 +182,15 @@ get_control_data(ClusterInfo *cluster)
 		}
 	}
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		resetwal_bin = "pg_resetxlog\" -n";
-	else
-		resetwal_bin = "pg_resetwal\" -n";
 	snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
 			 cluster->bindir,
-			 live_check ? "pg_controldata\"" : resetwal_bin,
+			 live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
 			 cluster->pgdata);
 	fflush(NULL);
 
 	if ((output = popen(cmd, "r")) == NULL)
 		pg_fatal("could not get control data using %s: %m", cmd);
 
-	/* Only in <= 9.2 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		cluster->controldata.data_checksum_version = PG_DATA_CHECKSUM_OFF;
-		got_data_checksum_version = true;
-	}
-
 	/* we have the result of cmd in "output". so parse it line by line now */
 	while (fgets(bufin, sizeof(bufin), output))
 	{
@@ -237,39 +218,6 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.cat_ver = str2uint(p);
 		}
-		else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			tli = str2uint(p);
-			got_tli = true;
-		}
-		else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			logid = str2uint(p);
-			got_log_id = true;
-		}
-		else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL)
-		{
-			p = strchr(p, ':');
-
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem", __LINE__);
-
-			p++;				/* remove ':' char */
-			segno = str2uint(p);
-			got_log_seg = true;
-		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
 			p = strchr(p, ':');
@@ -280,23 +228,12 @@ get_control_data(ClusterInfo *cluster)
 			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
 
-			/*
-			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
-			 * the catalog version of the change because the catalog version
-			 * is pulled from pg_controldata too, and it isn't worth adding an
-			 * order dependency for this --- we just check the string.
-			 */
-			if (strchr(p, '/') != NULL)
-				p = strchr(p, '/');
-			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
 				p = strchr(p, ':');
-			else
-				p = NULL;
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem", __LINE__);
 
-			p++;				/* remove '/' or ':' char */
+			p++;				/* remove ':' char */
 			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
@@ -568,22 +505,6 @@ get_control_data(ClusterInfo *cluster)
 	pg_free(lc_all);
 	pg_free(lc_messages);
 
-	/*
-	 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
-	 * file after reset as separate lines. Starting with 9.3, it reports the
-	 * WAL file name. If the old cluster is older than 9.3, we construct the
-	 * WAL file name from the xlogid and segno.
-	 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 902)
-	{
-		if (got_tli && got_log_id && got_log_seg)
-		{
-			snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X",
-					 tli, logid, segno);
-			got_nextxlogfile = true;
-		}
-	}
-
 	/*
 	 * Pre-v18 database clusters don't have the default char signedness
 	 * information. We use the char signedness of the platform where
@@ -602,14 +523,12 @@ get_control_data(ClusterInfo *cluster)
 	/* verify that we got all the mandatory pg_control data */
 	if (!got_xid || !got_oid ||
 		!got_multi || !got_oldestxid ||
-		(!got_oldestmulti &&
-		 cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+		!got_oldestmulti ||
 		!got_mxoff || (!live_check && !got_nextxlogfile) ||
 		!got_float8_pass_by_value || !got_align || !got_blocksz ||
 		!got_largesz || !got_walsz || !got_walseg || !got_ident ||
 		!got_index || !got_toast ||
-		(!got_large_object &&
-		 cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+		!got_large_object ||
 		!got_date_is_int || !got_data_checksum_version ||
 		(!got_default_char_signedness &&
 		 cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +549,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_multi)
 			pg_log(PG_REPORT, "  latest checkpoint next MultiXactId");
 
-		if (!got_oldestmulti &&
-			cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+		if (!got_oldestmulti)
 			pg_log(PG_REPORT, "  latest checkpoint oldest MultiXactId");
 
 		if (!got_oldestxid)
@@ -670,8 +588,7 @@ get_control_data(ClusterInfo *cluster)
 		if (!got_toast)
 			pg_log(PG_REPORT, "  maximum TOAST chunk size");
 
-		if (!got_large_object &&
-			cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+		if (!got_large_object)
 			pg_log(PG_REPORT, "  large-object chunk size");
 
 		if (!got_date_is_int)
@@ -724,8 +641,7 @@ check_control_data(ControlData *oldctrl,
 	if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
 		pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
 
-	/* large_object added in 9.5, so it might not exist in the old cluster */
-	if (oldctrl->large_object != 0 &&
+	if (oldctrl->large_object == 0 ||
 		oldctrl->large_object != newctrl->large_object)
 		pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
 
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
 	if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
 		pg_fatal("could not get pg_ctl version output from %s", cmd);
 
-	if (v1 < 10)
-	{
-		/* old style, e.g. 9.6.1 */
-		cluster->bin_version = v1 * 10000 + v2 * 100;
-	}
-	else
-	{
-		/* new style, e.g. 10.1 */
 		cluster->bin_version = v1 * 10000;
-	}
 }
 
 
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
 	check_single_dir(pg_data, "pg_subtrans");
 	check_single_dir(pg_data, PG_TBLSPC_DIR);
 	check_single_dir(pg_data, "pg_twophase");
-
-	/* pg_xlog has been renamed to pg_wal in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_xlog");
-	else
 		check_single_dir(pg_data, "pg_wal");
-
-	/* pg_clog has been renamed to pg_xact in v10 */
-	if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
-		check_single_dir(pg_data, "pg_clog");
-	else
 		check_single_dir(pg_data, "pg_xact");
 }
 
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
 	 */
 	get_bin_version(cluster);
 
-	/* pg_resetxlog has been renamed to pg_resetwal in version 10 */
-	if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
-		check_exec(cluster->bindir, "pg_resetxlog", check_versions);
-	else
 		check_exec(cluster->bindir, "pg_resetwal", check_versions);
 
 	if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
 #include <linux/fs.h>
 #endif
 
-#include "access/visibilitymapdefs.h"
 #include "common/file_perm.h"
 #include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
 
 
 /*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
 }
 
 
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table.  So, instead, we rewrite the old
- * visibility maps in the new format.  That way, the all-visible bits
- * remain set for the pages for which they were set previously.  The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
-					 const char *schemaName, const char *relName)
-{
-	int			src_fd;
-	int			dst_fd;
-	PGIOAlignedBlock buffer;
-	PGIOAlignedBlock new_vmbuf;
-	ssize_t		totalBytesRead = 0;
-	ssize_t		src_filesize;
-	int			rewriteVmBytesPerPage;
-	BlockNumber new_blkno = 0;
-	struct stat statbuf;
-
-	/* Compute number of old-format bytes per new page */
-	rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
-	if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if (fstat(src_fd, &statbuf) != 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
-				 schemaName, relName, fromfile);
-
-	if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
-					   pg_file_create_mode)) < 0)
-		pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
-				 schemaName, relName, tofile);
-
-	/* Save old file size */
-	src_filesize = statbuf.st_size;
-
-	/*
-	 * Turn each visibility map page into 2 pages one by one. Each new page
-	 * has the same page header as the old one.  If the last section of the
-	 * last page is empty, we skip it, mostly to avoid turning one-page
-	 * visibility maps for small relations into two pages needlessly.
-	 */
-	while (totalBytesRead < src_filesize)
-	{
-		ssize_t		bytesRead;
-		char	   *old_cur;
-		char	   *old_break;
-		char	   *old_blkend;
-		PageHeaderData pageheader;
-		bool		old_lastblk;
-
-		if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
-		{
-			if (bytesRead < 0)
-				pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
-						 schemaName, relName, fromfile);
-			else
-				pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
-						 schemaName, relName, fromfile);
-		}
-
-		totalBytesRead += BLCKSZ;
-		old_lastblk = (totalBytesRead == src_filesize);
-
-		/* Save the page header data */
-		memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
-		/*
-		 * These old_* variables point to old visibility map page. old_cur
-		 * points to current position on old page. old_blkend points to end of
-		 * old block.  old_break is the end+1 position on the old page for the
-		 * data that will be transferred to the current new page.
-		 */
-		old_cur = buffer.data + SizeOfPageHeaderData;
-		old_blkend = buffer.data + bytesRead;
-		old_break = old_cur + rewriteVmBytesPerPage;
-
-		while (old_break <= old_blkend)
-		{
-			char	   *new_cur;
-			bool		empty = true;
-			bool		old_lastpart;
-
-			/* First, copy old page header to new page */
-			memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
-			/* Rewriting the last part of the last old page? */
-			old_lastpart = old_lastblk && (old_break == old_blkend);
-
-			new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
-			/* Process old page bytes one by one, and turn it into new page. */
-			while (old_cur < old_break)
-			{
-				uint8		byte = *(uint8 *) old_cur;
-				uint16		new_vmbits = 0;
-				int			i;
-
-				/* Generate new format bits while keeping old information */
-				for (i = 0; i < BITS_PER_BYTE; i++)
-				{
-					if (byte & (1 << i))
-					{
-						empty = false;
-						new_vmbits |=
-							VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
-					}
-				}
-
-				/* Copy new visibility map bytes to new-format page */
-				new_cur[0] = (char) (new_vmbits & 0xFF);
-				new_cur[1] = (char) (new_vmbits >> 8);
-
-				old_cur++;
-				new_cur += BITS_PER_HEAPBLOCK;
-			}
-
-			/* If the last part of the last page is empty, skip writing it */
-			if (old_lastpart && empty)
-				break;
-
-			/* Set new checksum for visibility map page, if enabled */
-			if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
-				((PageHeader) new_vmbuf.data)->pd_checksum =
-					pg_checksum_page(new_vmbuf.data, new_blkno);
-
-			errno = 0;
-			if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
-			{
-				/* if write didn't set errno, assume problem is no disk space */
-				if (errno == 0)
-					errno = ENOSPC;
-				pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
-						 schemaName, relName, tofile);
-			}
-
-			/* Advance for next new page */
-			old_break += rewriteVmBytesPerPage;
-			new_blkno++;
-		}
-	}
-
-	/* Clean up */
-	close(dst_fd);
-	close(src_fd);
-}
-
 void
 check_file_clone(void)
 {
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
  * 32-bit offsets to the current format.
  *
  * Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format.  An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files.  (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
  *
  * Returns the new nextOffset value; the caller should set it in the new
  * control file.  The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	SlruSegState *members_writer;
 	char		dir[MAXPGPATH] = {0};
 	bool		prev_multixid_valid = false;
+	OldMultiXactReader *old_reader;
 
 	/*
 	 * The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 	 * Convert old multixids, if needed, by reading them one-by-one from the
 	 * old cluster.
 	 */
-	if (to_multi != from_multi)
-	{
-		OldMultiXactReader *old_reader;
-
 		old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
 										   old_cluster.controldata.chkpnt_nxtmulti,
 										   old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
 		}
 
 		FreeOldMultiXactReader(old_reader);
-	}
 
 	/* Write the final 'next' offset to the last SLRU page */
 	RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..c6af9035665 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -64,7 +64,7 @@ static void prepare_new_cluster(void);
 static void prepare_new_globals(void);
 static void create_new_objects(void);
 static void copy_xact_xlog_xid(void);
-static void set_frozenxids(bool minmxid_only);
+static void set_frozenxids(void);
 static void make_outputdirs(char *pgdata);
 static void setup(char *argv0);
 static void create_logical_replication_slots(void);
@@ -576,7 +576,7 @@ prepare_new_globals(void)
 	/*
 	 * Before we restore anything, set frozenxids of initdb-created tables.
 	 */
-	set_frozenxids(false);
+	set_frozenxids();
 
 	/*
 	 * Now restore global objects (roles and tablespaces).
@@ -714,13 +714,6 @@ create_new_objects(void)
 	end_progress_output();
 	check_ok();
 
-	/*
-	 * We don't have minmxids for databases or relations in pre-9.3 clusters,
-	 * so set those after we have restored the schema.
-	 */
-	if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
-		set_frozenxids(true);
-
 	/* update new_cluster info now that we have objects in the databases */
 	get_db_rel_and_slot_infos(&new_cluster);
 }
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
 	 * Copy old commit logs to new data dir. pg_clog has been renamed to
 	 * pg_xact in post-10 clusters.
 	 */
-	copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact",
-					  GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
-					  "pg_clog" : "pg_xact");
+	copy_subdir_files("pg_xact", "pg_xact");
 
 	prep_status("Setting oldest XID for new cluster");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
 	check_ok();
 
 	/* Copy or convert pg_multixact files */
-	Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
 	Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
 	if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
 	{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
 		 * Determine the range of multixacts to convert.
 		 */
 		nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
-		if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
-		{
-			/* Versions 9.3 - 18: convert all multixids  */
 			oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
-		}
-		else
-		{
-			/*
-			 * In PostgreSQL 9.2 and below, multitransactions were only used
-			 * for row locking, and as such don't need to be preserved during
-			 * upgrade.  In that case, we utilize rewrite_multixacts() just to
-			 * initialize new, empty files in the new format.
-			 *
-			 * It's important that the oldest multi is set to the latest value
-			 * used by the old system, so that multixact.c returns the empty
-			 * set for multis that might be present on disk.
-			 */
-			oldstMulti = nxtmulti;
-		}
 		/* handle wraparound */
 		if (nxtmulti < FirstMultiXactId)
 			nxtmulti = FirstMultiXactId;
@@ -906,26 +877,18 @@ copy_xact_xlog_xid(void)
 /*
  *	set_frozenxids()
  *
- * This is called on the new cluster before we restore anything, with
- * minmxid_only = false.  Its purpose is to ensure that all initdb-created
+ * This is called on the new cluster before we restore anything.
+ * Its purpose is to ensure that all initdb-created
  * vacuumable tables have relfrozenxid/relminmxid matching the old cluster's
  * xid/mxid counters.  We also initialize the datfrozenxid/datminmxid of the
  * built-in databases to match.
  *
  * As we create user tables later, their relfrozenxid/relminmxid fields will
  * be restored properly by the binary-upgrade restore script.  Likewise for
- * user-database datfrozenxid/datminmxid.  However, if we're upgrading from a
- * pre-9.3 database, which does not store per-table or per-DB minmxid, then
- * the relminmxid/datminmxid values filled in by the restore script will just
- * be zeroes.
- *
- * Hence, with a pre-9.3 source database, a second call occurs after
- * everything is restored, with minmxid_only = true.  This pass will
- * initialize all tables and databases, both those made by initdb and user
- * objects, with the desired minmxid value.  frozenxid values are left alone.
+ * user-database datfrozenxid/datminmxid.
  */
 static void
-set_frozenxids(bool minmxid_only)
+set_frozenxids(void)
 {
 	int			dbnum;
 	PGconn	   *conn,
@@ -935,14 +898,10 @@ set_frozenxids(bool minmxid_only)
 	int			i_datname;
 	int			i_datallowconn;
 
-	if (!minmxid_only)
 		prep_status("Setting frozenxid and minmxid counters in new cluster");
-	else
-		prep_status("Setting minmxid counter in new cluster");
 
 	conn_template1 = connectToServer(&new_cluster, "template1");
 
-	if (!minmxid_only)
 		/* set pg_database.datfrozenxid */
 		PQclear(executeQueryOrDie(conn_template1,
 								  "UPDATE pg_catalog.pg_database "
@@ -983,7 +942,6 @@ set_frozenxids(bool minmxid_only)
 
 		conn = connectToServer(&new_cluster, datname);
 
-		if (!minmxid_only)
 			/* set pg_class.relfrozenxid */
 			PQclear(executeQueryOrDie(conn,
 									  "UPDATE	pg_catalog.pg_class "
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
 #endif
 
 
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value.  pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
 /*
  * MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
  * catalog version.  pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
  */
 #define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
 
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
 /*
  * The control file was changed to have the default char signedness,
  * commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void		copyFileByRange(const char *src, const char *dst,
 							const char *schemaName, const char *relName);
 void		linkFile(const char *src, const char *dst,
 					 const char *schemaName, const char *relName);
-void		rewriteVisibilityMap(const char *fromfile, const char *tofile,
-								 const char *schemaName, const char *relName);
 void		check_file_clone(void);
 void		check_copy_file_range(void);
 void		check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
 
 /* version.c */
 
-bool		jsonb_9_4_check_applicable(ClusterInfo *cluster);
 bool		protocol_negotiation_supported(const ClusterInfo *cluster);
-void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
-											bool check_mode);
 
 void		report_extension_updates(ClusterInfo *cluster);
 
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
 #include "pg_upgrade.h"
 
 static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
 
 /*
  * The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 					   char *old_tablespace, char *new_tablespace)
 {
 	int			mapnum;
-	bool		vm_must_add_frozenbit = false;
-
-	/*
-	 * Do we need to rewrite visibilitymap?
-	 */
-	if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
-		new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
-		vm_must_add_frozenbit = true;
 
 	/* --swap has its own subroutine */
 	if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
 	{
-		/*
-		 * We don't support --swap to upgrade from versions that require
-		 * rewriting the visibility map.  We should've failed already if
-		 * someone tries to do that.
-		 */
-		Assert(!vm_must_add_frozenbit);
-
 		do_swap(maps, size, old_tablespace, new_tablespace);
 		return;
 	}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
 			strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
 		{
 			/* transfer primary file */
-			transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "");
 
 			/*
 			 * Copy/link any fsm and vm files, if they exist
 			 */
-			transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
-			transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+			transfer_relfile(&maps[mapnum], "_fsm");
+			transfer_relfile(&maps[mapnum], "_vm");
 		}
 	}
 }
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
 /*
  * transfer_relfile()
  *
- * Copy or link file from old cluster to new one.  If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
  */
 static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
 {
 	char		old_file[MAXPGPATH];
 	char		new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
 		/* Copying files might take some time, so give feedback. */
 		pg_log(PG_STATUS, "%s", old_file);
 
-		if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
-		{
-			/* Need to rewrite visibility map format */
-			pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
-				   old_file, new_file);
-			rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
-		}
-		else
 			switch (user_opts.transfer_mode)
 			{
 				case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 5d81e4e95b8..7da9dffe585 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -186,8 +186,7 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error)
 		snprintf(socket_string + strlen(socket_string),
 				 sizeof(socket_string) - strlen(socket_string),
 				 " -c %s='%s'",
-				 (GET_MAJOR_VERSION(cluster->major_version) <= 902) ?
-				 "unix_socket_directory" : "unix_socket_directories",
+				 "unix_socket_directories",
 				 cluster->sockdir);
 #endif
 
diff --git a/src/bin/pg_upgrade/t/006_transfer_modes.pl b/src/bin/pg_upgrade/t/006_transfer_modes.pl
index 6b7ab9f29a2..9efa5dc863c 100644
--- a/src/bin/pg_upgrade/t/006_transfer_modes.pl
+++ b/src/bin/pg_upgrade/t/006_transfer_modes.pl
@@ -17,15 +17,6 @@ sub test_mode
 	  PostgreSQL::Test::Cluster->new('old', install_path => $ENV{oldinstall});
 	my $new = PostgreSQL::Test::Cluster->new('new');
 
-	# --swap can't be used to upgrade from versions older than 10, so just skip
-	# the test if the old cluster version is too old.
-	if ($old->pg_version < 10 && $mode eq "--swap")
-	{
-		$old->clean_node();
-		$new->clean_node();
-		return;
-	}
-
 	if (defined($ENV{oldinstall}))
 	{
 		# Checksums are now enabled by default, but weren't before 18, so pass
@@ -38,14 +29,10 @@ sub test_mode
 	}
 	$new->init();
 
-	# allow_in_place_tablespaces is available as far back as v10.
-	if ($old->pg_version >= 10)
-	{
 		$new->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
 		$old->append_conf('postgresql.conf',
 			"allow_in_place_tablespaces = true");
-	}
 
 	# We can only test security labels if both the old and new installations
 	# have dummy_seclabel.
@@ -89,9 +76,6 @@ sub test_mode
 			"CREATE TABLE test4 AS SELECT generate_series(400, 502)");
 	}
 
-	# If the old cluster is >= v10, we can test in-place tablespaces.
-	if ($old->pg_version >= 10)
-	{
 		$old->safe_psql('postgres',
 			"CREATE TABLESPACE inplc_tblspc LOCATION ''");
 		$old->safe_psql('postgres',
@@ -101,7 +85,6 @@ sub test_mode
 		);
 		$old->safe_psql('testdb3',
 			"CREATE TABLE test6 AS SELECT generate_series(607, 711)");
-	}
 
 	# While we are here, test handling of large objects.
 	$old->safe_psql(
@@ -169,15 +152,12 @@ sub test_mode
 		}
 
 		# Tests for in-place tablespaces.
-		if ($old->pg_version >= 10)
-		{
 			$result =
 			  $new->safe_psql('postgres', "SELECT COUNT(*) FROM test5");
 			is($result, '104', "test5 data after pg_upgrade $mode");
 			$result =
 			  $new->safe_psql('testdb3', "SELECT COUNT(*) FROM test6");
 			is($result, '105', "test6 data after pg_upgrade $mode");
-		}
 
 		# Tests for large objects
 		$result = $new->safe_psql('postgres', "SELECT lo_get(4532)");
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
 #include "fe_utils/string_utils.h"
 #include "pg_upgrade.h"
 
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
-	/* JSONB changed its storage format during 9.4 beta */
-	if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
-		cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
-		return true;
-
-	return false;
-}
-
 /*
  * Older servers can't support newer protocol versions, so their connection
  * strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
 	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
 }
 
-/*
- * old_9_6_invalidate_hash_indexes()
- *	9.6 -> 10
- *	Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
-	int			dbnum;
-	FILE	   *script = NULL;
-	bool		found = false;
-	char	   *output_path = "reindex_hash.sql";
-
-	prep_status("Checking for hash indexes");
-
-	for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
-	{
-		PGresult   *res;
-		bool		db_used = false;
-		int			ntups;
-		int			rowno;
-		int			i_nspname,
-					i_relname;
-		DbInfo	   *active_db = &cluster->dbarr.dbs[dbnum];
-		PGconn	   *conn = connectToServer(cluster, active_db->db_name);
-
-		/* find hash indexes */
-		res = executeQueryOrDie(conn,
-								"SELECT n.nspname, c.relname "
-								"FROM	pg_catalog.pg_class c, "
-								"		pg_catalog.pg_index i, "
-								"		pg_catalog.pg_am a, "
-								"		pg_catalog.pg_namespace n "
-								"WHERE	i.indexrelid = c.oid AND "
-								"		c.relam = a.oid AND "
-								"		c.relnamespace = n.oid AND "
-								"		a.amname = 'hash'"
-			);
-
-		ntups = PQntuples(res);
-		i_nspname = PQfnumber(res, "nspname");
-		i_relname = PQfnumber(res, "relname");
-		for (rowno = 0; rowno < ntups; rowno++)
-		{
-			found = true;
-			if (!check_mode)
-			{
-				if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
-					pg_fatal("could not open file \"%s\": %m", output_path);
-				if (!db_used)
-				{
-					PQExpBufferData connectbuf;
-
-					initPQExpBuffer(&connectbuf);
-					appendPsqlMetaConnect(&connectbuf, active_db->db_name);
-					fputs(connectbuf.data, script);
-					termPQExpBuffer(&connectbuf);
-					db_used = true;
-				}
-				fprintf(script, "REINDEX INDEX %s.%s;\n",
-						quote_identifier(PQgetvalue(res, rowno, i_nspname)),
-						quote_identifier(PQgetvalue(res, rowno, i_relname)));
-			}
-		}
-
-		PQclear(res);
-
-		if (!check_mode && db_used)
-		{
-			/* mark hash indexes as invalid */
-			PQclear(executeQueryOrDie(conn,
-									  "UPDATE pg_catalog.pg_index i "
-									  "SET	indisvalid = false "
-									  "FROM	pg_catalog.pg_class c, "
-									  "		pg_catalog.pg_am a, "
-									  "		pg_catalog.pg_namespace n "
-									  "WHERE	i.indexrelid = c.oid AND "
-									  "		c.relam = a.oid AND "
-									  "		c.relnamespace = n.oid AND "
-									  "		a.amname = 'hash'"));
-		}
-
-		PQfinish(conn);
-	}
-
-	if (script)
-		fclose(script);
-
-	if (found)
-	{
-		report_status(PG_WARNING, "warning");
-		if (check_mode)
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  After upgrading, you will be given\n"
-				   "REINDEX instructions.");
-		else
-			pg_log(PG_WARNING, "\n"
-				   "Your installation contains hash indexes.  These indexes have different\n"
-				   "internal formats between your old and new clusters, so they must be\n"
-				   "reindexed with the REINDEX command.  The file\n"
-				   "    %s\n"
-				   "when executed by psql by the database superuser will recreate all invalid\n"
-				   "indexes; until then, none of these indexes will be used.",
-				   output_path);
-	}
-	else
-		check_ok();
-}
-
 /*
  * Callback function for processing results of query for
  * report_extension_updates()'s UpgradeTask.  If the query returned any rows,
-- 
2.50.1 (Apple Git-155)


--aHl3HfeiUuO3zeNC
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename=v6-0003-psql-bump-minimum-supported-version-to-v10.patch



^ permalink  raw  reply  [nested|flat] 80+ messages in thread

* [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error
@ 2026-04-23 09:27 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 80+ messages in thread

From: Bertrand Drouvot @ 2026-04-23 09:27 UTC (permalink / raw)

getObjectTypeDescription() and getObjectIdentityParts() are missing switch cases
for PropgraphElementLabelRelationId and PropgraphLabelPropertyRelationId, causing
them to hit the default case and error out with "unsupported object class".

During DROP PROPERTY GRAPH, this manifests when an event trigger is active,
because pg_event_trigger_ddl_commands() calls these functions. The same code
paths are also reachable via pg_identify_object() and pg_identify_object_as_address().

This commit adds the missing cases.

Test coverage is added in object_address.sql for these two catalog types and
in create_property_graph.sql covering all property graph object types.

A test in event_trigger.sql verifies that DROP PROPERTY GRAPH works correctly
when an event trigger is active.

Author: Bertrand Drouvot <[email protected]>
Reviewed-by: Michael Paquier <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://postgr.es/m/aej1DkLwhyZWmtxJ@bdtpg
---
 src/backend/catalog/objectaddress.c           | 93 +++++++++++++++++++
 .../expected/create_property_graph.out        | 78 ++++++++++++++++
 src/test/regress/expected/event_trigger.out   | 17 ++++
 src/test/regress/expected/object_address.out  | 10 +-
 .../regress/sql/create_property_graph.sql     | 23 +++++
 src/test/regress/sql/event_trigger.sql        | 13 +++
 src/test/regress/sql/object_address.sql       |  6 +-
 7 files changed, 238 insertions(+), 2 deletions(-)
  14.7% src/backend/catalog/
  75.5% src/test/regress/expected/
   9.7% src/test/regress/sql/

diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index c1862809577..e2d6d8f71f6 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -4901,10 +4901,18 @@ getObjectTypeDescription(const ObjectAddress *object, bool missing_ok)
 			appendStringInfoString(&buffer, "policy");
 			break;
 
+		case PropgraphElementLabelRelationId:
+			appendStringInfoString(&buffer, "property graph element label");
+			break;
+
 		case PropgraphElementRelationId:
 			appendStringInfoString(&buffer, "property graph element");
 			break;
 
+		case PropgraphLabelPropertyRelationId:
+			appendStringInfoString(&buffer, "property graph label property");
+			break;
+
 		case PropgraphLabelRelationId:
 			appendStringInfoString(&buffer, "property graph label");
 			break;
@@ -6161,6 +6169,49 @@ getObjectIdentityParts(const ObjectAddress *object,
 				break;
 			}
 
+		case PropgraphElementLabelRelationId:
+			{
+				Relation	ellabelDesc;
+				ScanKeyData skey[1];
+				SysScanDesc ellabelscan;
+				HeapTuple	tup;
+				Form_pg_propgraph_element_label pgelform;
+				ObjectAddress oa;
+
+				ellabelDesc = table_open(PropgraphElementLabelRelationId, AccessShareLock);
+				ScanKeyInit(&skey[0],
+							Anum_pg_propgraph_element_label_oid,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
+
+				ellabelscan = systable_beginscan(ellabelDesc,
+												 PropgraphElementLabelObjectIndexId,
+												 true, NULL, 1, skey);
+
+				tup = systable_getnext(ellabelscan);
+				if (!HeapTupleIsValid(tup))
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for element label %u",
+							 object->objectId);
+
+					systable_endscan(ellabelscan);
+					table_close(ellabelDesc, AccessShareLock);
+					break;
+				}
+
+				pgelform = (Form_pg_propgraph_element_label) GETSTRUCT(tup);
+
+				ObjectAddressSet(oa, PropgraphElementRelationId, pgelform->pgelelid);
+
+				appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+																	   objargs, false));
+
+				systable_endscan(ellabelscan);
+				table_close(ellabelDesc, AccessShareLock);
+				break;
+			}
+
 		case PropgraphElementRelationId:
 			{
 				HeapTuple	tup;
@@ -6184,6 +6235,48 @@ getObjectIdentityParts(const ObjectAddress *object,
 				break;
 			}
 
+		case PropgraphLabelPropertyRelationId:
+			{
+				Relation	lblpropDesc;
+				ScanKeyData skey[1];
+				SysScanDesc lblpropscan;
+				HeapTuple	tup;
+				Form_pg_propgraph_label_property plpform;
+				ObjectAddress oa;
+
+				lblpropDesc = table_open(PropgraphLabelPropertyRelationId,
+										 AccessShareLock);
+				ScanKeyInit(&skey[0],
+							Anum_pg_propgraph_label_property_oid,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
+
+				lblpropscan = systable_beginscan(lblpropDesc, PropgraphLabelPropertyObjectIndexId,
+												 true, NULL, 1, skey);
+
+				tup = systable_getnext(lblpropscan);
+				if (!HeapTupleIsValid(tup))
+				{
+					if (!missing_ok)
+						elog(ERROR, "could not find tuple for label property %u",
+							 object->objectId);
+
+					systable_endscan(lblpropscan);
+					table_close(lblpropDesc, AccessShareLock);
+					break;
+				}
+
+				plpform = (Form_pg_propgraph_label_property) GETSTRUCT(tup);
+
+				ObjectAddressSet(oa, PropgraphElementLabelRelationId, plpform->plpellabelid);
+				appendStringInfoString(&buffer, getObjectIdentityParts(&oa, objname,
+																	   objargs, false));
+
+				systable_endscan(lblpropscan);
+				table_close(lblpropDesc, AccessShareLock);
+				break;
+			}
+
 		case PropgraphLabelRelationId:
 			{
 				HeapTuple	tup;
diff --git a/src/test/regress/expected/create_property_graph.out b/src/test/regress/expected/create_property_graph.out
index bc9a596ec89..eb9e17fb727 100644
--- a/src/test/regress/expected/create_property_graph.out
+++ b/src/test/regress/expected/create_property_graph.out
@@ -752,6 +752,84 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
  type                    | create_property_graph_tests | g2   | create_property_graph_tests.g2
 (21 rows)
 
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+    WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+        SELECT classid, objid, objsubid,
+               refclassid, refobjid, refobjsubid
+        FROM pg_depend
+        WHERE refclassid = 'pg_class'::regclass AND
+              refobjid = 'create_property_graph_tests.gt'::regclass
+        UNION ALL
+        SELECT d.classid, d.objid, d.objsubid,
+               d.refclassid, d.refobjid, d.refobjsubid
+        FROM pg_depend d
+        JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+    )
+    SELECT pg_describe_object(classid, objid, objsubid) as obj,
+           pg_identify_object(classid, objid, objsubid) as ident,
+           pg_identify_object_as_address(classid, objid, objsubid) as addr
+    FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+                           obj                            |                                   ident                                    |                                    addr                                    
+----------------------------------------------------------+----------------------------------------------------------------------------+----------------------------------------------------------------------------
+ edge e of property graph gt                              | ("property graph element",,,"e of create_property_graph_tests.gt")         | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt                              | ("property graph element",,,"e of create_property_graph_tests.gt")         | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ edge e of property graph gt                              | ("property graph element",,,"e of create_property_graph_tests.gt")         | ("property graph element","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt                   | ("property graph element label",,,"e of create_property_graph_tests.gt")   | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt                   | ("property graph element label",,,"e of create_property_graph_tests.gt")   | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt                   | ("property graph element label",,,"e of create_property_graph_tests.gt")   | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of edge e of property graph gt                   | ("property graph element label",,,"e of create_property_graph_tests.gt")   | ("property graph element label","{create_property_graph_tests,gt,e}",{})
+ label e of property graph gt                             | ("property graph label",,,"e of create_property_graph_tests.gt")           | ("property graph label","{create_property_graph_tests,gt,e}",{})
+ label v1 of property graph gt                            | ("property graph label",,,"v1 of create_property_graph_tests.gt")          | ("property graph label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt               | ("property graph element label",,,"v1 of create_property_graph_tests.gt")  | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v1 of vertex v1 of property graph gt               | ("property graph element label",,,"v1 of create_property_graph_tests.gt")  | ("property graph element label","{create_property_graph_tests,gt,v1}",{})
+ label v2 of property graph gt                            | ("property graph label",,,"v2 of create_property_graph_tests.gt")          | ("property graph label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt               | ("property graph element label",,,"v2 of create_property_graph_tests.gt")  | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ label v2 of vertex v2 of property graph gt               | ("property graph element label",,,"v2 of create_property_graph_tests.gt")  | ("property graph element label","{create_property_graph_tests,gt,v2}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property a of property graph gt                          | ("property graph property",,,"a of create_property_graph_tests.gt")        | ("property graph property","{create_property_graph_tests,gt,a}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of label v1 of vertex v1 of property graph gt | ("property graph label property",,,"v1 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v1}",{})
+ property b of property graph gt                          | ("property graph property",,,"b of create_property_graph_tests.gt")        | ("property graph property","{create_property_graph_tests,gt,b}",{})
+ property c of label e of edge e of property graph gt     | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt     | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt     | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt     | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of label e of edge e of property graph gt     | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property c of property graph gt                          | ("property graph property",,,"c of create_property_graph_tests.gt")        | ("property graph property","{create_property_graph_tests,gt,c}",{})
+ property k1 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k1 of property graph gt                         | ("property graph property",,,"k1 of create_property_graph_tests.gt")       | ("property graph property","{create_property_graph_tests,gt,k1}",{})
+ property k2 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of label e of edge e of property graph gt    | ("property graph label property",,,"e of create_property_graph_tests.gt")  | ("property graph label property","{create_property_graph_tests,gt,e}",{})
+ property k2 of property graph gt                         | ("property graph property",,,"k2 of create_property_graph_tests.gt")       | ("property graph property","{create_property_graph_tests,gt,k2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property m of property graph gt                          | ("property graph property",,,"m of create_property_graph_tests.gt")        | ("property graph property","{create_property_graph_tests,gt,m}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of label v2 of vertex v2 of property graph gt | ("property graph label property",,,"v2 of create_property_graph_tests.gt") | ("property graph label property","{create_property_graph_tests,gt,v2}",{})
+ property n of property graph gt                          | ("property graph property",,,"n of create_property_graph_tests.gt")        | ("property graph property","{create_property_graph_tests,gt,n}",{})
+ type gt                                                  | (type,create_property_graph_tests,gt,create_property_graph_tests.gt)       | (type,{create_property_graph_tests.gt},{})
+ type gt[]                                                | (type,create_property_graph_tests,_gt,create_property_graph_tests.gt[])    | (type,{create_property_graph_tests.gt[]},{})
+ vertex v1 of property graph gt                           | ("property graph element",,,"v1 of create_property_graph_tests.gt")        | ("property graph element","{create_property_graph_tests,gt,v1}",{})
+ vertex v2 of property graph gt                           | ("property graph element",,,"v2 of create_property_graph_tests.gt")        | ("property graph element","{create_property_graph_tests,gt,v2}",{})
+(52 rows)
+
 \a\t
 SELECT pg_get_propgraphdef('g2'::regclass);
 CREATE PROPERTY GRAPH create_property_graph_tests.g2
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 065f586310f..ed8d5df397e 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -173,6 +173,23 @@ NOTICE:  test_event_trigger: ddl_command_end CREATE USER MAPPING
 alter default privileges for role regress_evt_user
  revoke delete on tables from regress_evt_user;
 NOTICE:  test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+NOTICE:  test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE:  test_event_trigger: ddl_command_end CREATE TABLE
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+NOTICE:  test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE:  test_event_trigger: ddl_command_end CREATE TABLE
+CREATE PROPERTY GRAPH gx
+	VERTEX TABLES (
+		t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+		t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+NOTICE:  test_event_trigger: ddl_command_end CREATE PROPERTY GRAPH
+DROP PROPERTY GRAPH gx;
+NOTICE:  test_event_trigger: ddl_command_end DROP PROPERTY GRAPH
+DROP TABLE t1x, t2x;
+NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regress_evt_user;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 97227d67a54..5e53f0b092e 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -67,7 +67,9 @@ DECLARE
 BEGIN
     FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
         ('toast table column'), ('view column'), ('materialized view column'),
-        ('property graph element'), ('property graph label'), ('property graph property')
+        ('property graph element'), ('property graph element label'),
+        ('property graph label'), ('property graph label property'),
+        ('property graph property')
     LOOP
         BEGIN
             PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -84,7 +86,9 @@ WARNING:  error for toast table column: unsupported object type "toast table col
 WARNING:  error for view column: unsupported object type "view column"
 WARNING:  error for materialized view column: unsupported object type "materialized view column"
 WARNING:  error for property graph element: unsupported object type "property graph element"
+WARNING:  error for property graph element label: unrecognized object type "property graph element label"
 WARNING:  error for property graph label: unsupported object type "property graph label"
+WARNING:  error for property graph label property: unrecognized object type "property graph label property"
 WARNING:  error for property graph property: unsupported object type "property graph property"
 -- miscellaneous other errors
 select * from pg_get_object_address('operator of access method', '{btree,integer_ops,1}', '{int4,bool}');
@@ -593,7 +597,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
     ('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
     ('pg_policy'::regclass, 0, 0), -- no policy
     ('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+    ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
     ('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+    ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
     ('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
     ('pg_publication'::regclass, 0, 0), -- no publication
     ('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
@@ -653,6 +659,8 @@ ORDER BY objects.classid, objects.objid, objects.objsubid;
 ("(""parameter ACL"",,,)")|("(""parameter ACL"",,)")|NULL
 ("(""property graph element"",,,)")|("(""property graph element"",,)")|NULL
 ("(""property graph label"",,,)")|("(""property graph label"",,)")|NULL
+("(""property graph element label"",,,)")|("(""property graph element label"",,)")|NULL
 ("(""property graph property"",,,)")|("(""property graph property"",,)")|NULL
+("(""property graph label property"",,,)")|("(""property graph label property"",,)")|NULL
 -- restore normal output mode
 \a\t
diff --git a/src/test/regress/sql/create_property_graph.sql b/src/test/regress/sql/create_property_graph.sql
index 241f93df302..9608dd5c4af 100644
--- a/src/test/regress/sql/create_property_graph.sql
+++ b/src/test/regress/sql/create_property_graph.sql
@@ -298,6 +298,29 @@ SELECT (pg_identify_object(classid, objid, objsubid)).*
           refobjid = 'create_property_graph_tests.g2'::regclass
     ORDER BY 1, 2, 3, 4;
 
+-- test object address functions with recursive dependency walk
+-- covers all property graph object types including indirect dependents
+SELECT *
+FROM (
+    WITH RECURSIVE deps (classid, objid, objsubid, refclassid, refobjid, refobjsubid) AS (
+        SELECT classid, objid, objsubid,
+               refclassid, refobjid, refobjsubid
+        FROM pg_depend
+        WHERE refclassid = 'pg_class'::regclass AND
+              refobjid = 'create_property_graph_tests.gt'::regclass
+        UNION ALL
+        SELECT d.classid, d.objid, d.objsubid,
+               d.refclassid, d.refobjid, d.refobjsubid
+        FROM pg_depend d
+        JOIN deps dp ON d.refclassid = dp.classid AND d.refobjid = dp.objid AND d.refobjsubid = dp.objsubid
+    )
+    SELECT pg_describe_object(classid, objid, objsubid) as obj,
+           pg_identify_object(classid, objid, objsubid) as ident,
+           pg_identify_object_as_address(classid, objid, objsubid) as addr
+    FROM deps
+) s
+ORDER BY obj COLLATE "C", ident::text COLLATE "C";
+
 \a\t
 SELECT pg_get_propgraphdef('g2'::regclass);
 SELECT pg_get_propgraphdef('g3'::regclass);
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index 32e9bb58c5e..271f443378b 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -143,6 +143,19 @@ create user mapping for regress_evt_user server useless_server;
 alter default privileges for role regress_evt_user
  revoke delete on tables from regress_evt_user;
 
+-- DROP PROPERTY GRAPH should work with event trigger in place
+CREATE TABLE t1x (a int PRIMARY KEY, b text);
+CREATE TABLE t2x (i int PRIMARY KEY, j text);
+
+CREATE PROPERTY GRAPH gx
+	VERTEX TABLES (
+		t1x KEY (a) LABEL l1 PROPERTIES (b AS p1),
+		t2x KEY (i) LABEL l2 PROPERTIES (j AS p1)
+);
+
+DROP PROPERTY GRAPH gx;
+DROP TABLE t1x, t2x;
+
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regress_evt_user;
 
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 1bbe9457c1c..27109d421c7 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -67,7 +67,9 @@ DECLARE
 BEGIN
     FOR objtype IN VALUES ('toast table'), ('index column'), ('sequence column'),
         ('toast table column'), ('view column'), ('materialized view column'),
-        ('property graph element'), ('property graph label'), ('property graph property')
+        ('property graph element'), ('property graph element label'),
+        ('property graph label'), ('property graph label property'),
+        ('property graph property')
     LOOP
         BEGIN
             PERFORM pg_get_object_address(objtype, '{one}', '{}');
@@ -282,7 +284,9 @@ WITH objects (classid, objid, objsubid) AS (VALUES
     ('pg_parameter_acl'::regclass, 0, 0), -- no parameter ACL
     ('pg_policy'::regclass, 0, 0), -- no policy
     ('pg_propgraph_element'::regclass, 0, 0), -- no property graph element
+    ('pg_propgraph_element_label'::regclass, 0, 0), -- no property graph element label
     ('pg_propgraph_label'::regclass, 0, 0), -- no property graph label
+    ('pg_propgraph_label_property'::regclass, 0, 0), -- no property graph label property
     ('pg_propgraph_property'::regclass, 0, 0), -- no property graph property
     ('pg_publication'::regclass, 0, 0), -- no publication
     ('pg_publication_namespace'::regclass, 0, 0), -- no publication namespace
-- 
2.34.1


--R84XrR4Hgw0dH3Ec--





^ permalink  raw  reply  [nested|flat] 80+ messages in thread


end of thread, other threads:[~2026-04-23 09:27 UTC | newest]

Thread overview: 80+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-08-07 23:51 [PATCH v3 10/10] regress: Check for postgres startup completion more often Andres Freund <[email protected]>
2023-08-07 23:51 [PATCH v3 10/10] regress: Check for postgres startup completion more often Andres Freund <[email protected]>
2023-08-07 23:51 [PATCH v1 7/9] regress: Check for postgres startup completion more often Andres Freund <[email protected]>
2023-08-07 23:51 [PATCH v1 7/9] regress: Check for postgres startup completion more often Andres Freund <[email protected]>
2023-08-07 23:51 [PATCH v1 7/9] regress: Check for postgres startup completion more often Andres Freund <[email protected]>
2026-01-14 17:54 [PATCH v1 2/3] Move x86 popcount code to pg_popcount_x86_64.c. Nathan Bossart <[email protected]>
2026-01-14 17:54 [PATCH v2 2/4] Move x86 popcount code to pg_popcount_x86_64.c. Nathan Bossart <[email protected]>
2026-01-14 17:54 [PATCH v2 2/4] Move x86 popcount code to pg_popcount_x86_64.c. Nathan Bossart <[email protected]>
2026-01-14 17:54 [PATCH v1 2/3] Move x86 popcount code to pg_popcount_x86_64.c. Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v2 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v5 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v3 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-17 18:20 [PATCH v6 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
2026-04-23 09:27 [PATCH v3] Fix DROP PROPERTY GRAPH "unsupported object class" error Bertrand Drouvot <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox