From a982da6e56e1af22ef66397ddef997c15eb3612f Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Wed, 13 Mar 2024 18:25:48 +0100 Subject: [PATCH v13 4/9] 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) do not, and the same is true for many unspported PostgreSQL server versions. Connecting to such other applications would fail if we would change which minor protocol version libpq would request by default. In preparation of adding a new minor protocol version in a future commit, this patch adds a way of actually using that new protocol version such applications using libpq by introducing the max_protocol_version connection option. This way people can specify what protocol version to request from the server. Once more of the ecosystem supports the NegotiateProtocolVersion message we might want to change the default to the latest minor version, but for now we keep 3.s0 hardcoded as the default. --- doc/src/sgml/libpq.sgml | 27 ++++++++++++++ src/interfaces/libpq/fe-connect.c | 59 ++++++++++++++++++++++++++++++- src/interfaces/libpq/libpq-int.h | 2 ++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 70a6a796b69..f02f8b64eb1 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2307,6 +2307,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. + + + diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a56d85149a9..7c75e4c46da 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", "", 3, /* sizeof("3.2") = 3 */ + 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 minor, + major; + + minor = 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; + } + major = 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(minor, major); + 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 */ @@ -2849,7 +2906,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 3886204c708..f7f4eca11d9 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