From 3b5e43a02c680f7e179b09f1d653e7cf6538dbca Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Mon, 23 Feb 2026 15:28:32 -0800
Subject: [PATCH v2] pg_upgrade: Use max_protocol_version=3.0 for older servers

The grease patch in 4966bd3ed found its first problem: prior to the
February 2018 patch releases, no server knew how to negotiate protocol
versions, so pg_upgrade needs to take that into account when speaking to
those older servers.

This will be true even after the grease feature is reverted; we don't
need anyone to trip over this again in the future. Backpatch so that all
supported versions of pg_upgrade can gracefully handle an update to the
default protocol version. (This is needed for any distributions that
link older binaries against newer libpqs, such as Debian.) Branches
prior to 18 need an additional version check, for the existence of
max_protocol_version.

Per buildfarm member crake.

Discussion: https://postgr.es/m/CAOYmi%2B%3D4QhCjssfNEoZVK8LPtWxnfkwT5p-PAeoxtG9gpNjqOQ%40mail.gmail.com
Backpatch-through: 14
---
 src/bin/pg_upgrade/pg_upgrade.h |  1 +
 src/bin/pg_upgrade/dump.c       |  6 +++++-
 src/bin/pg_upgrade/server.c     |  2 ++
 src/bin/pg_upgrade/version.c    | 31 +++++++++++++++++++++++++++++++
 4 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 7afa96716ec..44aad96de7b 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -456,6 +456,7 @@ bool		check_for_data_types_usage(ClusterInfo *cluster,
 bool		check_for_data_type_usage(ClusterInfo *cluster,
 									  const char *type_name,
 									  const char *output_path);
+bool		protocol_negotiation_supported(const ClusterInfo *cluster);
 void		old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster);
 void		old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster);
 void		old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c
index 6c8c82dca89..a698519029e 100644
--- a/src/bin/pg_upgrade/dump.c
+++ b/src/bin/pg_upgrade/dump.c
@@ -21,9 +21,10 @@ generate_old_dump(void)
 
 	/* run new pg_dumpall binary for globals */
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
-			  "\"%s/pg_dumpall\" %s --globals-only --quote-all-identifiers "
+			  "\"%s/pg_dumpall\" %s%s --globals-only --quote-all-identifiers "
 			  "--binary-upgrade %s -f \"%s/%s\"",
 			  new_cluster.bindir, cluster_conn_opts(&old_cluster),
+			  protocol_negotiation_supported(&old_cluster) ? "" : " -d \"max_protocol_version=3.0\"",
 			  log_opts.verbose ? "--verbose" : "",
 			  log_opts.dumpdir,
 			  GLOBALS_DUMP_FILE);
@@ -43,6 +44,9 @@ generate_old_dump(void)
 		initPQExpBuffer(&connstr);
 		appendPQExpBufferStr(&connstr, "dbname=");
 		appendConnStrVal(&connstr, old_db->db_name);
+		if (!protocol_negotiation_supported(&old_cluster))
+			appendPQExpBufferStr(&connstr, " max_protocol_version=3.0");
+
 		initPQExpBuffer(&escaped_connstr);
 		appendShellString(&escaped_connstr, connstr.data);
 		termPQExpBuffer(&connstr);
diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c
index 0bc3d2806b8..75d40626b48 100644
--- a/src/bin/pg_upgrade/server.c
+++ b/src/bin/pg_upgrade/server.c
@@ -71,6 +71,8 @@ get_db_conn(ClusterInfo *cluster, const char *db_name)
 		appendPQExpBufferStr(&conn_opts, " host=");
 		appendConnStrVal(&conn_opts, cluster->sockdir);
 	}
+	if (!protocol_negotiation_supported(cluster))
+		appendPQExpBufferStr(&conn_opts, " max_protocol_version=3.0");
 
 	conn = PQconnectdb(conn_opts.data);
 	termPQExpBuffer(&conn_opts);
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 403a6d7cfaa..e869d526ba1 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -241,6 +241,37 @@ old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
 		check_ok();
 }
 
+/*
+ * Older servers can't support newer protocol versions, so their connection
+ * strings will need to lock max_protocol_version to 3.0.
+ */
+bool
+protocol_negotiation_supported(const ClusterInfo *cluster)
+{
+	/*
+	 * Back-branch-specific complication: for libpq versions prior to PG18,
+	 * max_protocol_version isn't supported. But we also don't need to worry
+	 * about newer protocol versions being used in that case, so just lie and
+	 * return true.
+	 *
+	 * (Checking for a libpq version that's newer than this branch looks very
+	 * strange, but distributions are allowed to link older pg_upgrade
+	 * binaries against the newest release of libpq.)
+	 */
+	if (PQlibVersion() < 180000)
+		return true;
+
+	/*
+	 * The February 2018 patch release (9.3.21, 9.4.16, 9.5.11, 9.6.7, and
+	 * 10.2) added support for NegotiateProtocolVersion. But ClusterInfo only
+	 * has information about the major version number. To ensure we can still
+	 * upgrade older unpatched servers, just assume anything prior to PG11
+	 * can't negotiate. It's not possible for those servers to make use of
+	 * newer protocols anyway, so nothing is lost.
+	 */
+	return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
+}
+
 /*
  * old_9_6_invalidate_hash_indexes()
  *	9.6 -> 10
-- 
2.34.1

