diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 36fd327d4b9..89ac680efd5 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -270,6 +270,18 @@
+
+ 3.9999
+ -
+ Reserved for protocol greasing. libpq may use this version, which
+ is higher than any minor version the project ever expects to use, to
+ test that servers and middleware properly implement protocol version
+ negotiation. Servers must not add special-case
+ logic for this version; they should simply compare it to their latest
+ supported version (which will always be smaller) and downgrade via a
+ NegotiateProtocolVersion message.
+
+
3.1
-
@@ -353,6 +365,17 @@
otherwise continue the connection.
+
+
+ _pq_.test_protocol_negotiation
+ Reserved for protocol greasing. libpq may send this extension to
+ test that servers and middleware properly implement protocol extension
+ negotiation. Servers must not add special-case
+ logic for this parameter; they should simply send the list of all
+ unsupported options (including this one) via a NegotiateProtocolVersion
+ message.
+
+
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 1bbe5b9ee45..a29c9c94d79 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -104,6 +104,16 @@ is_unixsock_path(const char *path)
*/
#define PG_PROTOCOL_RESERVED_31 PG_PROTOCOL(3,1)
+/*
+ * PG_PROTOCOL_GREASE is an intentionally unsupported protocol version used
+ * for "greasing" (the practice of sending valid, but extraneous or otherwise
+ * unusual, messages to keep peer implementations honest). This helps ensure
+ * that servers properly implement protocol version negotiation. Version 3.9999
+ * was chosen since it is safely within the valid range, it is representable
+ * via PQfullProtocolVersion, and it is unlikely to ever be needed in practice.
+ */
+#define PG_PROTOCOL_GREASE PG_PROTOCOL(3,9999)
+
/*
* A client can send a cancel-current-operation request to the postmaster.
* This is uglier than sending it directly to the client's backend, but it
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 103428033ef..90bbb2eba1f 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1451,7 +1451,19 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
if (pqGetInt(&num, 4, conn) != 0)
goto eof;
- /* Check the protocol version */
+ /*
+ * Check the protocol version.
+ *
+ * PG_PROTOCOL_GREASE is intentionally unsupported and reserved. It's
+ * higher than any real version, so check for that first, to get the most
+ * specific error message. Then check the upper and lower bounds.
+ */
+ if (their_version == PG_PROTOCOL_GREASE)
+ {
+ libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested \"grease\" protocol version 3.9999");
+ goto failure;
+ }
+
if (their_version > conn->pversion)
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message: server requested downgrade to a higher-numbered version");