diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 4d3b6446c41..999fa060188 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -22,10 +22,18 @@
PostgreSQL 7.4 and later. For descriptions
of the earlier protocol versions, see previous releases of the
PostgreSQL documentation. A single server
- can support multiple protocol versions. The initial
- startup-request message tells the server which protocol version the
- client is attempting to use, and then the server follows that protocol
- if it is able.
+ can support multiple protocol versions. The initial startup-request
+ message tells the server which protocol version the client is attempting to
+ use. If the major version requested by the client is not supported by
+ the server, the connection will be rejected (for example, this would occur
+ if the client requested protocol version 4.0, which does not exist as of
+ this writing). If the minor version requested by the client is not
+ supported by the server (e.g. the client requests version 3.1, but the
+ server supports only 3.0), the server may either reject the connection or
+ may respond with a NegotiateProtocolVersion message containing the highest
+ minor protocol version which it supports. The client may then choose either
+ to continue with the connection using the specified protocol version or
+ to abort the connection.
@@ -406,6 +414,21 @@
+
+ NegotiateProtocolVersion
+
+
+ The server does not support the minor protocol version requested
+ by the client, but does support an earlier version of the protocol;
+ this message indicates the highest supported minor version. This
+ message will also be sent if the client requested unsupported protocol
+ options (i.e. beginning with _pq_.) in the
+ startup packet. This message will be followed by an ErrorResponse or
+ a message indicating the success or failure of authentication.
+
+
+
+
@@ -420,8 +443,10 @@
for further messages from the server. In this phase a backend process
is being started, and the frontend is just an interested bystander.
It is still possible for the startup attempt
- to fail (ErrorResponse), but in the normal case the backend will send
- some ParameterStatus messages, BackendKeyData, and finally ReadyForQuery.
+ to fail (ErrorResponse) or the server to decline support for the requested
+ minor protocol version (NegotiateProtocolVersion), but in the normal case
+ the backend will send some ParameterStatus messages, BackendKeyData, and
+ finally ReadyForQuery.
@@ -4715,6 +4740,74 @@ GSSResponse (F)
+
+
+NegotiateProtocolVersion (B)
+
+
+
+
+
+
+
+ Byte1('v')
+
+
+
+ Identifies the message as a protocol version negotiation
+ message.
+
+
+
+
+
+ Int32
+
+
+
+ Length of message contents in bytes, including self.
+
+
+
+
+
+ Int32
+
+
+
+ Newest minor protocol version supported by the server
+ for the major protocol version requested by the client.
+
+
+
+
+
+ Int32
+
+
+
+ Number of protocol options not recognized by the server.
+
+
+
+
+ Then, for protocol option not recognized by the server, there
+ is the following:
+
+
+
+ String
+
+
+
+ The option name.
+
+
+
+
+
+
+
@@ -5670,11 +5763,13 @@ StartupMessage (F)
- In addition to the above, any run-time parameter that can be
- set at backend start time might be listed. Such settings
- will be applied during backend start (after parsing the
- command-line arguments if any). The values will act as
- session defaults.
+ In addition to the above, others parameter may be listed.
+ Parameter names beginning with _pq_. are
+ reserved for use as protocol extensions, while others are
+ treated as run-time parameters to be set at backend start
+ time. Such settings will be applied during backend start
+ (after parsing the command-line arguments if any) and will
+ act as session defaults.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 9906a85bc0e..a3d49171730 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -101,6 +101,7 @@
#include "lib/ilist.h"
#include "libpq/auth.h"
#include "libpq/libpq.h"
+#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pg_getopt.h"
@@ -412,6 +413,7 @@ static void ExitPostmaster(int status) pg_attribute_noreturn();
static int ServerLoop(void);
static int BackendStartup(Port *port);
static int ProcessStartupPacket(Port *port, bool SSLdone);
+static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
static void processCancelRequest(Port *port, void *pkt);
static int initMasks(fd_set *rmask);
static void report_fork_failure_to_client(Port *port, int errnum);
@@ -2052,12 +2054,9 @@ retry1:
*/
FrontendProtocol = proto;
- /* Check we can handle the protocol the frontend is using. */
-
+ /* Check that the major protocol version is in range. */
if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
- PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
- (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
- PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
+ PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
ereport(FATAL,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
@@ -2079,6 +2078,7 @@ retry1:
if (PG_PROTOCOL_MAJOR(proto) >= 3)
{
int32 offset = sizeof(ProtocolVersion);
+ List *unrecognized_protocol_options = NIL;
/*
* Scan packet body for name/option pairs. We can assume any string
@@ -2128,6 +2128,16 @@ retry1:
valptr),
errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
}
+ else if (strncmp(nameptr, "_pq_.", 5) == 0)
+ {
+ /*
+ * Any option beginning with _pq_. is reserved for use as a
+ * protocol-level option, but at present no such options are
+ * defined.
+ */
+ unrecognized_protocol_options =
+ lappend(unrecognized_protocol_options, pstrdup(nameptr));
+ }
else
{
/* Assume it's a generic GUC option */
@@ -2147,6 +2157,16 @@ retry1:
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("invalid startup packet layout: expected terminator as last byte")));
+
+ /*
+ * If the client requested a newer protocol version or if the client
+ * requested any protocol options we didn't recognize, let them know
+ * the newest minor protocol version we do support and the names of any
+ * unrecognized options.
+ */
+ if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
+ unrecognized_protocol_options != NIL)
+ SendNegotiateProtocolVersion(unrecognized_protocol_options);
}
else
{
@@ -2260,6 +2280,34 @@ retry1:
return STATUS_OK;
}
+/*
+ * Send a NegotiateProtocolVersion to the client. This lets the client know
+ * that they have requested a newer minor protocol version than we are able
+ * to speak. We'll speak the highest version we know about; the client can,
+ * of course, abandon the connection if that's a problem.
+ *
+ * We also include in the response a list of protocol options we didn't
+ * understand. This allows clients to include optional parameters that might
+ * be present either in newer protocol versions or third-party protocol
+ * extensions without fear of having to reconnect if those options are not
+ * understood, while at the same time making certain that the client is aware
+ * of which options were actually accepted.
+ */
+static void
+SendNegotiateProtocolVersion(List *unrecognized_protocol_options)
+{
+ StringInfoData buf;
+ ListCell *lc;
+
+ pq_beginmessage(&buf, 'v'); /* NegotiateProtocolVersion */
+ pq_sendint32(&buf, PG_PROTOCOL_LATEST);
+ pq_sendint32(&buf, list_length(unrecognized_protocol_options));
+ foreach(lc, unrecognized_protocol_options)
+ pq_sendstring(&buf, lfirst(lc));
+ pq_endmessage(&buf);
+
+ /* no need to flush, some other message will follow */
+}
/*
* The client has sent a cancel request packet, not a normal