v15-0004-libpq-Add-max_protocol_version-connection-option.patch

application/octet-stream

Filename: v15-0004-libpq-Add-max_protocol_version-connection-option.patch
Type: application/octet-stream
Part: 2
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 v15-0004
Subject: libpq: Add max_protocol_version connection option
File+
doc/src/sgml/libpq.sgml 37 0
src/interfaces/libpq/fe-connect.c 58 1
src/interfaces/libpq/libpq-int.h 2 0
From 3863ef3db73ccd08b3972058a8bfd90ec8265d76 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <postgres@jeltef.nl>
Date: Wed, 14 Aug 2024 18:25:16 +0200
Subject: [PATCH v15 4/9] libpq: Add max_protocol_version connection option

All officially supported version of the PostgreSQL server send the
NegotiateProtocolVersion message when an unsupported minor protocol
version is requested by a client. But many other applications that
implement the PostgreSQL protocol (connection poolers, or other
databases) do not, and the same is true for many unspported PostgreSQL
server versions. Connecting to such other applications thus fails if a
client requests a protocol version different than 3.0.

This patch adds a max_protocol_version connection option to libpq that
specifies the protocol version that libpq should request from the
server. Currently all allowed values result in the use of 3.0, but that
will be changed in a future commit that bumps the protocol version. Even
after that version bump the default will likely stay 3.0 for the time
being. Once more of the ecosystem supports the NegotiateProtocolVersion
message we might want to change the default to the latest minor version.
---
 doc/src/sgml/libpq.sgml           | 37 +++++++++++++++++++
 src/interfaces/libpq/fe-connect.c | 59 ++++++++++++++++++++++++++++++-
 src/interfaces/libpq/libpq-int.h  |  2 ++
 3 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index a609a8cff5..a782340e61 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -2335,6 +2335,33 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry id="libpq-connect-max-protocol-version" xreflabel="max_protocol_version">
+      <term><literal>max_protocol_version</literal></term>
+      <listitem>
+       <para>
+        Specifies the protocol version to request from the server.
+        The default is to use version <literal>3.0</literal> of the
+        <productname>PostgreSQL</productname> protocol, unless the connection
+        string specifies a feature that relies on a higher protocol version, in
+        that case the latest version supported by libpq is used. If the server
+        does not support the requested protocol version of the client the
+        connection will be automatically downgraded to a lower minor protocol
+        version, which the server does support. After the connection attempt has
+        completed you can use <xref linkend="libpq-PQprotocolVersion"/> to find
+        out which exact protocol version was negotiated.
+       </para>
+
+       <para>
+        The current supported values are
+        <literal>3.0</literal>
+        and <literal>latest</literal>. The <literal>latest</literal> value is
+        equivalent to the latest protocol version that is supported by the used
+        libpq version, which currently is <literal>3.0</literal>, but this will
+        change in future libpq releases.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
@@ -9136,6 +9163,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
       linkend="libpq-connect-load-balance-hosts"/> connection parameter.
      </para>
     </listitem>
+
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGMAXPROTOCOLVERSION</envar></primary>
+      </indexterm>
+      <envar>PGMAXPROTOCOLVERSION</envar> behaves the same as the <xref
+      linkend="libpq-connect-max-protocol-version"/> connection parameter.
+     </para>
+    </listitem>
    </itemizedlist>
   </para>
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5c8096697c..73891a125c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -364,6 +364,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,
+		"Max-Protocol-Version", "", 6,	/* sizeof("latest") = 6 */
+	offsetof(struct pg_conn, max_protocol_version)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -1834,6 +1839,58 @@ pqConnectOptions2(PGconn *conn)
 		}
 	}
 
+	if (conn->max_protocol_version)
+	{
+		if (strcmp(conn->max_protocol_version, "latest") == 0)
+		{
+			conn->max_pversion = PG_PROTOCOL_LATEST;
+		}
+		else
+		{
+			char	   *end;
+			int			major,
+						minor;
+
+			major = 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;
+			}
+			minor = strtol(&end[1], &end, 10);
+			if (*end != '\0')
+			{
+				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(major, minor);
+			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
+	{
+		/*
+		 * To not break connecting to older servers/poolers that do not yet
+		 * support NegotiateProtocolVersion, default to the 3.0 protocol at
+		 * least for a while longer.
+		 */
+		conn->max_pversion = PG_PROTOCOL(3, 0);
+	}
+
 	/*
 	 * Resolve special "auto" client_encoding from the locale
 	 */
@@ -2836,7 +2893,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(3, 0);
+		conn->pversion = conn->max_pversion;
 		conn->send_appname = true;
 		conn->failed_enc_methods = 0;
 		conn->current_enc_method = 0;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 03e4da40ba..65b8df2efc 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -427,6 +427,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
@@ -517,6 +518,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