From f61851ef5fc0b859a1285f6abd8e829f30ee5390 Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio 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 + + + max_protocol_version + + + 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 PostgreSQL + versions, certain connection poolers and some other databases speaking + the PostgreSQL protocol. So this connection + option can be used as an escape hatch when connecting to such servers + with libpq. + + + 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