v12-0006-libpq-Add-max_protocol_version-connection-option.patch

application/octet-stream

Filename: v12-0006-libpq-Add-max_protocol_version-connection-option.patch
Type: application/octet-stream
Part: 9
Message: Re: Add new protocol message to change GUCs for usage with future protocol-only GUCs

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v12-0006
Subject: libpq: Add max_protocol_version connection option
File+
doc/src/sgml/libpq.sgml 24 0
src/interfaces/libpq/fe-connect.c 35 2
src/interfaces/libpq/libpq-int.h 2 0
From f61851ef5fc0b859a1285f6abd8e829f30ee5390 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Wed, 13 Mar 2024 18:25:48 +0100
Subject: [PATCH v12 06/14] libpq: Add max_protocol_version connection option

All officially supported PostgreSQL servers send the
NegotiateProtocolVersion message when an unsupported minor protocol
version is requested by the client. But other applications implementing
the PosgreSQL protocol (connection poolers, or other databases) might
not, and the same is true for many unspported PostgreSQL server
versions. This patch adds an escape hatch for people wanting to connect
to such applications using libpq by introducing the max_protocol_version
connection option. This way people can manually specify what protocol
version to request from the server.
---
 doc/src/sgml/libpq.sgml           | 24 ++++++++++++++++++++
 src/interfaces/libpq/fe-connect.c | 37 +++++++++++++++++++++++++++++--
 src/interfaces/libpq/libpq-int.h  |  2 ++
 3 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index a74b502dbf2..dbf2a7659c1 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2259,6 +2259,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry id="libpq-max-protocol-version" xreflabel="load_balance_hosts">
+      <term><literal>max_protocol_version</literal></term>
+      <listitem>
+       <para>
+        Specifies the protocol version to request from the server. The argument
+        to this connection option is a single number: the result of multiplying
+        the major version number by 10000 and adding the minor version. For
+        example to request version 3.1 the provided argument should be 30001.
+        The default is to use the latest protocol version supported by libpq.
+        Normally there's no need to provide this argument, even when connecting
+        to PostgreSQL servers that don't support the latest protocol. The
+        reason for that is that all supported PostgreSQL server versions
+        automatically negotiate the highest minor protocol version supported by
+        both the client and server. But some servers might simply close
+        the connection when an unsupported protocol version is requested. This
+        problem only applies to unsupported <productname>PostgreSQL</productname>
+        versions, certain connection poolers and some other databases speaking
+        the <productname>PostgreSQL</productname> protocol. So this connection
+        option can be used as an escape hatch when connecting to such servers
+        with libpq.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 881c01d842c..7267c0dd687 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -359,6 +359,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Load-Balance-Hosts", "", 8,	/* sizeof("disable") = 8 */
 	offsetof(struct pg_conn, load_balance_hosts)},
 
+	{"max_protocol_version", "PGMAXPROTOCOLVERSION",
+		NULL, NULL,
+		"Load-Balance-Hosts", "", 8,	/* sizeof("30001") = 8 */
+	offsetof(struct pg_conn, max_protocol_version)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -1774,6 +1779,34 @@ pqConnectOptions2(PGconn *conn)
 		}
 	}
 
+	if (conn->max_protocol_version)
+	{
+		char	   *end;
+		int			val;
+
+		val = strtol(conn->max_protocol_version, &end, 10);
+		if (*end)
+		{
+			conn->status = CONNECTION_BAD;
+			libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
+									"max_protocol_version",
+									conn->max_protocol_version);
+			return false;
+		}
+		conn->max_pversion = PG_PROTOCOL(val / 10000, val % 10000);
+		if (conn->max_pversion > PG_PROTOCOL_LATEST ||
+			conn->max_pversion < PG_PROTOCOL_EARLIEST)
+		{
+			conn->status = CONNECTION_BAD;
+			libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
+									"max_protocol_version",
+									conn->max_protocol_version);
+			return false;
+		}
+	}
+	else
+		conn->max_pversion = PG_PROTOCOL_LATEST;
+
 	/*
 	 * Resolve special "auto" client_encoding from the locale
 	 */
@@ -2789,7 +2822,7 @@ keep_going:						/* We will come back to here until there is
 		 * must persist across individual connection attempts, but we must
 		 * reset them when we start to consider a new server.
 		 */
-		conn->pversion = PG_PROTOCOL_LATEST;
+		conn->pversion = conn->max_pversion;
 		conn->send_appname = true;
 #ifdef USE_SSL
 		/* initialize these values based on SSL mode */
@@ -3827,7 +3860,7 @@ keep_going:						/* We will come back to here until there is
 					}
 
 					/* neither -- server shouldn't have sent it */
-					if (!(conn->pversion < PG_PROTOCOL_LATEST) && !conn->unsupported_pextension_params)
+					if (!(conn->pversion < conn->max_pversion) && !conn->unsupported_pextension_params)
 					{
 						libpq_append_conn_error(conn, "invalid %s message", "NegotiateProtocolVersion");
 						goto error_return;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index b6dbcd64c5a..ccb58d2099d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -408,6 +408,7 @@ struct pg_conn
 	char	   *target_session_attrs;	/* desired session properties */
 	char	   *require_auth;	/* name of the expected auth method */
 	char	   *load_balance_hosts; /* load balance over hosts */
+	char	   *max_protocol_version;	/* maximum used protocol version */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
@@ -495,6 +496,7 @@ struct pg_conn
 	AddrInfo   *addr;			/* the array of addresses for the currently
 								 * tried host */
 	bool		send_appname;	/* okay to send application_name? */
+	ProtocolVersion max_pversion;	/* protocol version to request */
 
 	/* Miscellaneous stuff */
 	int			be_pid;			/* PID of backend --- needed for cancels */
-- 
2.34.1