aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2017-11-21 13:56:24 -0500
committerRobert Haas <rhaas@postgresql.org>2017-11-21 14:34:26 -0500
commitc703aa6258997d22ca7338bc221c31ff83feefb7 (patch)
treea0601cff9daccbacfef71db343a231e112b30a28 /src
parent2cfafabe64bae2d7923ab9863786d7bcc7cdf793 (diff)
downloadpostgresql-c703aa6258997d22ca7338bc221c31ff83feefb7.tar.gz
postgresql-c703aa6258997d22ca7338bc221c31ff83feefb7.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/backend/postmaster/postmaster.c58
1 files changed, 53 insertions, 5 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 69d661fdd00..d791a331f5c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -99,6 +99,7 @@
#include "libpq/auth.h"
#include "libpq/ip.h"
#include "libpq/libpq.h"
+#include "libpq/pqformat.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pg_getopt.h"
@@ -398,6 +399,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);
@@ -2000,12 +2002,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",
@@ -2027,6 +2026,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
@@ -2076,6 +2076,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 */
@@ -2095,6 +2105,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
{
@@ -2208,6 +2228,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_sendint(&buf, PG_PROTOCOL_LATEST, 4);
+ pq_sendint(&buf, list_length(unrecognized_protocol_options), 4);
+ 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