From 60ed7f5624b007b7ab7b27167fa44f0aa51692ab Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <postgres@jeltef.nl>
Date: Thu, 15 Jan 2026 09:58:07 +0100
Subject: [PATCH v5 4/5] libpq: Reconnect if requested grease but version is
 newer

While max_protocol_version=grease is mostly meant for testing, people
might still use it in production environments. So it seems better to
support reconnecting automatically if the server responds with a
downgrade to a higher version than libpq supports.
---
 src/interfaces/libpq/fe-connect.c   | 23 ++++++++++++++++---
 src/interfaces/libpq/fe-protocol3.c | 34 +++++++++++++++++++----------
 2 files changed, 43 insertions(+), 14 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index fdc05cd3243..738655488f3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -4196,10 +4196,27 @@ keep_going:						/* We will come back to here until there is
 						libpq_append_conn_error(conn, "received duplicate protocol negotiation message");
 						goto error_return;
 					}
-					if (pqGetNegotiateProtocolVersion3(conn))
 					{
-						/* pqGetNegotiateProtocolVersion3 set error already */
-						goto error_return;
+						int			rc = pqGetNegotiateProtocolVersion3(conn);
+
+						if (rc == 1)
+						{
+							/*
+							 * pqGetNegotiateProtocolVersion3 set error
+							 * already
+							 */
+							goto error_return;
+						}
+						if (rc == 2)
+						{
+							/*
+							 * Server proposed newer version than libpq
+							 * supports; retry with the latest supported
+							 * version.
+							 */
+							need_new_connection = true;
+							goto keep_going;
+						}
 					}
 					conn->pversion_negotiated = true;
 
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index c0e0142798a..b7e30516516 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1452,6 +1452,8 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding)
  * Entry: 'v' message type and length have already been consumed.
  * Exit: returns 0 if successfully consumed message.
  *		 returns 1 on failure. The error message is filled in.
+ *		 returns 2 if server proposed a newer version and we were using GREASE;
+ *		   caller should retry the connection with the latest supported version.
  */
 int
 pqGetNegotiateProtocolVersion3(PGconn *conn)
@@ -1465,6 +1467,21 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
 	if (pqGetInt(&their_version, 4, conn) != 0)
 		goto eof;
 
+	/*
+	 * If the server responds with a version newer than what this libpq
+	 * supports, and we were using GREASE, retry with the latest supported
+	 * version. This can happen if a future server legitimately supports a
+	 * newer minor version than us. We don't try to parse the rest of the
+	 * message since we don't know what format a newer protocol version might
+	 * use. The GREASE checks for protocol extensions will still happen on the
+	 * retry.
+	 */
+	if (their_version > PG_PROTOCOL_LATEST && PG_PROTOCOL_IS_GREASE(conn->pversion))
+	{
+		conn->max_pversion = PG_PROTOCOL_LATEST;
+		return 2;
+	}
+
 	if (pqGetInt(&num, 4, conn) != 0)
 		goto eof;
 
@@ -1496,22 +1513,17 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
 		goto failure;
 	}
 
-	/*
-	 * If the server responds with a version newer than what this libpq
-	 * supports, disconnect. This can happen if we sent a GREASE version and a
-	 * future server legitimately supports a newer minor version than us.
-	 */
-	if (their_version > PG_PROTOCOL_LATEST)
+	if (num < 0)
 	{
-		libpq_append_conn_error(conn, "server proposed protocol version 3.%d, but libpq only supports up to 3.%d",
-								PG_PROTOCOL_MINOR(their_version),
-								PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
+		libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported negative number of unsupported parameters");
 		goto failure;
 	}
 
-	if (num < 0)
+	if (their_version > PG_PROTOCOL_LATEST)
 	{
-		libpq_append_conn_error(conn, "received invalid protocol negotiation message: server reported negative number of unsupported parameters");
+		libpq_append_conn_error(conn, "server proposed protocol version 3.%d, but libpq only supports up to 3.%d",
+								PG_PROTOCOL_MINOR(their_version),
+								PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
 		goto failure;
 	}
 
-- 
2.52.0

