From e8bc0d2acfcc036b7b6c22454118464d9c500a12 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jacob.champion@enterprisedb.com>
Date: Fri, 6 Feb 2026 14:23:31 -0800
Subject: [PATCH v7 2/2] squash! libpq: Grease the protocol by default

Add more information to help a user who hits a grease failure.
Still needs a final URL.
---
 src/interfaces/libpq/libpq-int.h    |  1 +
 src/interfaces/libpq/fe-connect.c   |  1 +
 src/interfaces/libpq/fe-misc.c      | 19 +++++++++++++++++++
 src/interfaces/libpq/fe-protocol3.c | 10 ++++++++++
 4 files changed, 31 insertions(+)

diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index fb6a7cbf15d..bd7eb59f5f8 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -958,6 +958,7 @@ extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigne
 
 extern void libpq_append_error(PQExpBuffer errorMessage, const char *fmt,...) pg_attribute_printf(2, 3);
 extern void libpq_append_conn_error(PGconn *conn, const char *fmt,...) pg_attribute_printf(2, 3);
+extern void libpq_append_grease_info(PGconn *conn);
 
 /*
  * These macros are needed to let error-handling code be portable between
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c42f38cbc99..418c71a9aa8 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -4388,6 +4388,7 @@ keep_going:						/* We will come back to here until there is
 					conn->pversion == PG_PROTOCOL_GREASE)
 				{
 					libpq_append_conn_error(conn, "server incorrectly accepted \"grease\" protocol version 3.9999 without negotiation");
+					libpq_append_grease_info(conn);
 					goto error_return;
 				}
 
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 5e54353fbfe..e749aac6783 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -1423,3 +1423,22 @@ libpq_append_conn_error(PGconn *conn, const char *fmt,...)
 
 	appendPQExpBufferChar(&conn->errorMessage, '\n');
 }
+
+/*
+ * For 19beta only, some protocol errors will have additional information
+ * appended to help with the "grease" campaign.
+ */
+void
+libpq_append_grease_info(PGconn *conn)
+{
+	/* translator: %s is a URL */
+	libpq_append_conn_error(conn,
+							"\tThis indicates a bug in either the server being contacted\n"
+							"\tor a proxy handling the connection. Please consider\n"
+							"\treporting this to the maintainers of that software.\n"
+							"\tFor more information, including instructions on how to\n"
+							"\twork around this issue for now, visit\n"
+							"\t\t%s",
+	/* TODO */
+							"https://www.postgresql.org/docs/devel/libpq-connect.html#LIBPQ-CONNECT-MAX-PROTOCOL-VERSION");
+}
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index fc011e89450..8c1fda5caf0 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -1447,6 +1447,13 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
 	bool		found_test_protocol_negotiation;
 	bool		expect_test_protocol_negotiation;
 
+	/*
+	 * During 19beta only, if protocol grease is in use, assume that it's the
+	 * cause of any invalid messages encountered below. We'll print extra
+	 * information for the end user in that case.
+	 */
+	bool		need_grease_info = (conn->max_pversion == PG_PROTOCOL_GREASE);
+
 	if (pqGetInt(&their_version, 4, conn) != 0)
 		goto eof;
 
@@ -1506,6 +1513,7 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
 								PG_PROTOCOL_MAJOR(conn->min_pversion),
 								PG_PROTOCOL_MINOR(conn->min_pversion));
 
+		need_grease_info = false;	/* this is valid server behavior */
 		goto failure;
 	}
 
@@ -1561,6 +1569,8 @@ pqGetNegotiateProtocolVersion3(PGconn *conn)
 eof:
 	libpq_append_conn_error(conn, "received invalid protocol negotiation message: message too short");
 failure:
+	if (need_grease_info)
+		libpq_append_grease_info(conn);
 	conn->asyncStatus = PGASYNC_READY;
 	pqSaveErrorResult(conn);
 	return 1;
-- 
2.34.1

