From 4815717f793d74c10a0f92139e9aec7399bc929d Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Wed, 13 Mar 2024 18:25:48 +0100 Subject: [PATCH v14 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 a609a8cff58..edbceae9f34 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2335,6 +2335,33 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname + + + max_protocol_version + + + Specifies the protocol version to request from the server. + The default is to use version 3.0 of the + PostgreSQL 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 to find + out which exact protocol version was negotiated. + + + + The current supported values are + 3.0 + and latest. The latest value is + equivalent to the latest protocol version that is supported by the used + libpq version, which currently is 3.0, but this will + change in future libpq releases. + + + @@ -9136,6 +9163,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) linkend="libpq-connect-load-balance-hosts"/> connection parameter. + + + + + PGLOADBALANCEHOSTS + + PGLOADBALANCEHOSTS behaves the same as the connection parameter. + + diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 816dde114e4..8634f276871 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 f36d76bf3fe..2e3edf6acd7 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -415,6 +415,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 @@ -503,6 +504,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