Skip to content

Commit 294136d

Browse files
committed
Provide for forward compatibility with future minor protocol versions.
Previously, any attempt to request a 3.x protocol version other than 3.0 would lead to a hard connection failure, which made the minor protocol version really no different from the major protocol version and precluded gentle protocol version breaks. Instead, when the client requests a 3.x protocol version where x is greater than 0, send the new NegotiateProtocolVersion message to convey that we support only 3.0. This makes it possible to introduce new minor protocol versions without requiring a connection retry when the server is older. In addition, if the startup packet includes name/value pairs where the name starts with "_pq_.", assume that those are protocol options, not GUCs. Include those we don't support (i.e. all of them, at present) in the NegotiateProtocolVersion message so that the client knows they were not understood. This makes it possible for the client to request previously-unsupported features without bumping the protocol version at all; the client can tell from the server's response whether the option was understood. It will take some time before servers that support these new facilities become common in the wild; to speed things up and make things easier for a future 3.1 protocol version, back-patch to all supported releases. Robert Haas and Badrul Chowdhury Discussion: http://postgr.es/m/BN6PR21MB0772FFA0CBD298B76017744CD1730@BN6PR21MB0772.namprd21.prod.outlook.com Discussion: http://postgr.es/m/30788.1498672033@sss.pgh.pa.us
1 parent 13f2bdb commit 294136d

File tree

2 files changed

+158
-16
lines changed

2 files changed

+158
-16
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@
2222
<productname>PostgreSQL</productname> 7.4 and later. For descriptions
2323
of the earlier protocol versions, see previous releases of the
2424
<productname>PostgreSQL</productname> documentation. A single server
25-
can support multiple protocol versions. The initial
26-
startup-request message tells the server which protocol version the
27-
client is attempting to use, and then the server follows that protocol
28-
if it is able.
25+
can support multiple protocol versions. The initial startup-request
26+
message tells the server which protocol version the client is attempting to
27+
use. If the major version requested by the client is not supported by
28+
the server, the connection will be rejected (for example, this would occur
29+
if the client requested protocol version 4.0, which does not exist as of
30+
this writing). If the minor version requested by the client is not
31+
supported by the server (e.g. the client requests version 3.1, but the
32+
server supports only 3.0), the server may either reject the connection or
33+
may respond with a NegotiateProtocolVersion message containing the highest
34+
minor protocol version which it supports. The client may then choose either
35+
to continue with the connection using the specified protocol version or
36+
to abort the connection.
2937
</para>
3038

3139
<para>
@@ -366,6 +374,20 @@
366374
</listitem>
367375
</varlistentry>
368376

377+
<varlistentry>
378+
<term>NegotiateProtocolVersion</term>
379+
<listitem>
380+
<para>
381+
The server does not support the minor protocol version requested
382+
by the client, but does support an earlier version of the protocol;
383+
this message indicates the highest supported minor version. This
384+
message will also be sent if the client requested unsupported protocol
385+
options (i.e. beginning with <literal>_pq_.</literal>) in the
386+
startup packet. This message will be followed by an ErrorResponse or
387+
a message indicating the success or failure of authentication.
388+
</para>
389+
</listitem>
390+
</varlistentry>
369391
</variablelist>
370392
</para>
371393

@@ -380,8 +402,10 @@
380402
for further messages from the server. In this phase a backend process
381403
is being started, and the frontend is just an interested bystander.
382404
It is still possible for the startup attempt
383-
to fail (ErrorResponse), but in the normal case the backend will send
384-
some ParameterStatus messages, BackendKeyData, and finally ReadyForQuery.
405+
to fail (ErrorResponse) or the server to decline support for the requested
406+
minor protocol version (NegotiateProtocolVersion), but in the normal case
407+
the backend will send some ParameterStatus messages, BackendKeyData, and
408+
finally ReadyForQuery.
385409
</para>
386410

387411
<para>
@@ -3906,6 +3930,74 @@ FunctionCallResponse (B)
39063930
</listitem>
39073931
</varlistentry>
39083932

3933+
<varlistentry>
3934+
<term>
3935+
NegotiateProtocolVersion (B)
3936+
</term>
3937+
<listitem>
3938+
<para>
3939+
3940+
<variablelist>
3941+
<varlistentry>
3942+
<term>
3943+
Byte1('v')
3944+
</term>
3945+
<listitem>
3946+
<para>
3947+
Identifies the message as a protocol version negotiation
3948+
message.
3949+
</para>
3950+
</listitem>
3951+
</varlistentry>
3952+
<varlistentry>
3953+
<term>
3954+
Int32
3955+
</term>
3956+
<listitem>
3957+
<para>
3958+
Length of message contents in bytes, including self.
3959+
</para>
3960+
</listitem>
3961+
</varlistentry>
3962+
<varlistentry>
3963+
<term>
3964+
Int32
3965+
</term>
3966+
<listitem>
3967+
<para>
3968+
Newest minor protocol version supported by the server
3969+
for the major protocol version requested by the client.
3970+
</para>
3971+
</listitem>
3972+
</varlistentry>
3973+
<varlistentry>
3974+
<term>
3975+
Int32
3976+
</term>
3977+
<listitem>
3978+
<para>
3979+
Number of protocol options not recognized by the server.
3980+
</para>
3981+
</listitem>
3982+
</varlistentry>
3983+
</variablelist>
3984+
Then, for protocol option not recognized by the server, there
3985+
is the following:
3986+
<variablelist>
3987+
<varlistentry>
3988+
<term>
3989+
String
3990+
</term>
3991+
<listitem>
3992+
<para>
3993+
The option name.
3994+
</para>
3995+
</listitem>
3996+
</varlistentry>
3997+
</variablelist>
3998+
</para>
3999+
</listitem>
4000+
</varlistentry>
39094001

39104002
<varlistentry>
39114003
<term>
@@ -4730,11 +4822,13 @@ StartupMessage (F)
47304822
</varlistentry>
47314823
</variablelist>
47324824

4733-
In addition to the above, any run-time parameter that can be
4734-
set at backend start time might be listed. Such settings
4735-
will be applied during backend start (after parsing the
4736-
command-line options if any). The values will act as
4737-
session defaults.
4825+
In addition to the above, others parameter may be listed.
4826+
Parameter names beginning with <literal>_pq_.</literal> are
4827+
reserved for use as protocol extensions, while others are
4828+
treated as run-time parameters to be set at backend start
4829+
time. Such settings will be applied during backend start
4830+
(after parsing the command-line arguments if any) and will
4831+
act as session defaults.
47384832
</para>
47394833
</listitem>
47404834
</varlistentry>

src/backend/postmaster/postmaster.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
#include "libpq/auth.h"
100100
#include "libpq/ip.h"
101101
#include "libpq/libpq.h"
102+
#include "libpq/pqformat.h"
102103
#include "libpq/pqsignal.h"
103104
#include "miscadmin.h"
104105
#include "pg_getopt.h"
@@ -399,6 +400,7 @@ static void ExitPostmaster(int status) __attribute__((noreturn));
399400
static int ServerLoop(void);
400401
static int BackendStartup(Port *port);
401402
static int ProcessStartupPacket(Port *port, bool SSLdone);
403+
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
402404
static void processCancelRequest(Port *port, void *pkt);
403405
static int initMasks(fd_set *rmask);
404406
static void report_fork_failure_to_client(Port *port, int errnum);
@@ -1995,12 +1997,9 @@ ProcessStartupPacket(Port *port, bool SSLdone)
19951997
*/
19961998
FrontendProtocol = proto;
19971999

1998-
/* Check we can handle the protocol the frontend is using. */
1999-
2000+
/* Check that the major protocol version is in range. */
20002001
if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
2001-
PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
2002-
(PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
2003-
PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
2002+
PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST))
20042003
ereport(FATAL,
20052004
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20062005
errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
@@ -2022,6 +2021,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
20222021
if (PG_PROTOCOL_MAJOR(proto) >= 3)
20232022
{
20242023
int32 offset = sizeof(ProtocolVersion);
2024+
List *unrecognized_protocol_options = NIL;
20252025

20262026
/*
20272027
* Scan packet body for name/option pairs. We can assume any string
@@ -2069,6 +2069,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
20692069
errmsg("invalid value for parameter \"replication\""),
20702070
errhint("Valid values are: false, 0, true, 1, database.")));
20712071
}
2072+
else if (strncmp(nameptr, "_pq_.", 5) == 0)
2073+
{
2074+
/*
2075+
* Any option beginning with _pq_. is reserved for use as a
2076+
* protocol-level option, but at present no such options are
2077+
* defined.
2078+
*/
2079+
unrecognized_protocol_options =
2080+
lappend(unrecognized_protocol_options, pstrdup(nameptr));
2081+
}
20722082
else
20732083
{
20742084
/* Assume it's a generic GUC option */
@@ -2088,6 +2098,16 @@ ProcessStartupPacket(Port *port, bool SSLdone)
20882098
ereport(FATAL,
20892099
(errcode(ERRCODE_PROTOCOL_VIOLATION),
20902100
errmsg("invalid startup packet layout: expected terminator as last byte")));
2101+
2102+
/*
2103+
* If the client requested a newer protocol version or if the client
2104+
* requested any protocol options we didn't recognize, let them know
2105+
* the newest minor protocol version we do support and the names of any
2106+
* unrecognized options.
2107+
*/
2108+
if (PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST) ||
2109+
unrecognized_protocol_options != NIL)
2110+
SendNegotiateProtocolVersion(unrecognized_protocol_options);
20912111
}
20922112
else
20932113
{
@@ -2201,6 +2221,34 @@ ProcessStartupPacket(Port *port, bool SSLdone)
22012221
return STATUS_OK;
22022222
}
22032223

2224+
/*
2225+
* Send a NegotiateProtocolVersion to the client. This lets the client know
2226+
* that they have requested a newer minor protocol version than we are able
2227+
* to speak. We'll speak the highest version we know about; the client can,
2228+
* of course, abandon the connection if that's a problem.
2229+
*
2230+
* We also include in the response a list of protocol options we didn't
2231+
* understand. This allows clients to include optional parameters that might
2232+
* be present either in newer protocol versions or third-party protocol
2233+
* extensions without fear of having to reconnect if those options are not
2234+
* understood, while at the same time making certain that the client is aware
2235+
* of which options were actually accepted.
2236+
*/
2237+
static void
2238+
SendNegotiateProtocolVersion(List *unrecognized_protocol_options)
2239+
{
2240+
StringInfoData buf;
2241+
ListCell *lc;
2242+
2243+
pq_beginmessage(&buf, 'v'); /* NegotiateProtocolVersion */
2244+
pq_sendint(&buf, PG_PROTOCOL_LATEST, 4);
2245+
pq_sendint(&buf, list_length(unrecognized_protocol_options), 4);
2246+
foreach(lc, unrecognized_protocol_options)
2247+
pq_sendstring(&buf, lfirst(lc));
2248+
pq_endmessage(&buf);
2249+
2250+
/* no need to flush, some other message will follow */
2251+
}
22042252

22052253
/*
22062254
* The client has sent a cancel request packet, not a normal

0 commit comments

Comments
 (0)