From a5ef6cc738123b6ad2032a20ab0a8cc983cf2f0e Mon Sep 17 00:00:00 2001
From: Badrul Chowdhury <bachow@microsoft.com>
Date: Wed, 18 Oct 2017 15:26:33 -0700
Subject: [PATCH] BE changes for pgwire v3.1.

---
 src/backend/postmaster/postmaster.c | 80 +++++++++++++++++++++++++++++++++++--
 src/include/libpq/libpq-be.h        |  1 +
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 95180b2ef5..1eafdea87b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -387,6 +387,7 @@ static DNSServiceRef bonjour_sdref = NULL;
 /*
  * postmaster.c - function prototypes
  */
+static int	NegotiateServerProtocol(Port *port);
 static void CloseServerPorts(int status, Datum arg);
 static void unlink_external_pid_file(int status, Datum arg);
 static void getInstallationPaths(const char *argv0);
@@ -1368,6 +1369,48 @@ PostmasterMain(int argc, char *argv[])
 	abort();					/* not reached */
 }
 
+static int
+NegotiateServerProtocol(Port *port)
+{
+	StringInfoData buf;
+	char	   *name;
+	ListCell   *optional_params;
+
+	/*
+	 * NegotiateServerProtocol packet structure
+	 *
+	 * [ 'Y' | msgLength | min_version | max_version | param_list_len | list
+	 * of param names ]
+	 */
+
+	/* PG message type */
+	pq_beginmessage(&buf, 'Y');
+
+	/* Protocol version numbers */
+	pq_sendint(&buf, PG_PROTOCOL_EARLIEST, sizeof(int32));	/* min */
+	pq_sendint(&buf, PG_PROTOCOL_LATEST, sizeof(int32));	/* max */
+
+	/* Length of parameter list; parameter list consists of (key, value) pairs */
+	pq_sendint(&buf, list_length(port->optional_parameters) / 2, sizeof(int32));
+
+	optional_params = list_head(port->optional_parameters);
+	while (optional_params)
+	{
+		/* First comes key, which we need. */
+		name = lfirst(optional_params);
+		optional_params = lnext(optional_params);
+
+		/* Then comes value, which we don't need. */
+		optional_params = lnext(optional_params);
+
+		pq_sendstring(&buf, name);
+	}
+
+	pq_endmessage(&buf);
+
+	/* Ensure that the message buffer is flushed */
+	pq_flush();
+}
 
 /*
  * on_proc_exit callback to close server's listen sockets
@@ -2050,12 +2093,10 @@ retry1:
 	 */
 	FrontendProtocol = proto;
 
-	/* Check we can handle the protocol the frontend is using. */
+	/* Check we can handle the major protocol the frontend is using. */
 
 	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",
@@ -2126,6 +2167,13 @@ retry1:
 									valptr),
 							 errhint("Valid values are: \"false\", 0, \"true\", 1, \"database\".")));
 			}
+			else if (strlen(nameptr) > 4 && strncmp(nameptr, "_pq_", 4) == 0)
+			{
+				port->optional_parameters = lappend(port->optional_parameters,
+													pstrdup(nameptr));
+				port->optional_parameters = lappend(port->optional_parameters,
+													pstrdup(valptr));
+			}
 			else
 			{
 				/* Assume it's a generic GUC option */
@@ -2145,9 +2193,33 @@ retry1:
 			ereport(FATAL,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
 					 errmsg("invalid startup packet layout: expected terminator as last byte")));
+
+		/*
+		 * Need to negotiate pgwire protocol if FE version is not the same
+		 * as BE version and FE version is not 3.0
+		 */
+		if (FrontendProtocol != PG_PROTOCOL_LATEST
+			&& FrontendProtocol != PG_PROTOCOL(3, 0))
+		{
+			/* Negotiate parameters after all the error-checking is done */
+			if (NegotiateServerProtocol(port))
+				return STATUS_ERROR;
+		}
 	}
 	else
 	{
+		/* Check we can handle the minor protocol the frontend is using. */
+
+		if (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
+			PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
+			ereport(FATAL,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
+							PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
+							PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
+							PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
+							PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))));
+
 		/*
 		 * Get the parameters from the old-style, fixed-width-fields startup
 		 * packet as C strings.  The packet destination was cleared first so a
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 7bde744d51..8b2ef62580 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -137,6 +137,7 @@ typedef struct Port
 	char	   *user_name;
 	char	   *cmdline_options;
 	List	   *guc_options;
+	List	   *optional_parameters;
 
 	/*
 	 * Information that needs to be held during the authentication cycle.
-- 
2.13.2.windows.1

