diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 4d3b6446c4..999fa06018 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 9906a85bc0..a3d4917173 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