From bf371b501580cfa300c5198cc11ac67acc7631c3 Mon Sep 17 00:00:00 2001
From: Badrul Chowdhury <bachow@microsoft.com>
Date: Mon, 25 Sep 2017 13:42:44 -0700
Subject: [PATCH] =?UTF-8?q?Introducing=20pgwire=20v3.1:=201.=20adds=20supp?=
 =?UTF-8?q?ort=20for=20backend=20(BE)=20to=20accept=20optional=20parameter?=
 =?UTF-8?q?s,=20ie=20parameters=20that=20have=20=E2=80=9C=5Fpq=5F=E2=80=9D?=
 =?UTF-8?q?=20as=20a=20proper=20prefix=20in=20their=20names=202.=20creates?=
 =?UTF-8?q?=20data=20structure=20for=20passing=20in=20additional=20paramet?=
 =?UTF-8?q?ers=20in=20FE,=20adding=20command=20line=20support=20is=20out?=
 =?UTF-8?q?=20of=20scope=20of=20this=20item=203.=20enhances=20FE->BE=20pro?=
 =?UTF-8?q?tocol=20negotiation:=20adds=20support=20for=20newer=20FE=20to?=
 =?UTF-8?q?=20connect=20to=20older=20BE=20while=20maintaining=20back-compa?=
 =?UTF-8?q?tibility,=20ie=20same=20FE=20<->=20BE,=20older=20FE=20to=20newe?=
 =?UTF-8?q?r=20BE=20work=20as=20before?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/backend/postmaster/postmaster.c | 112 ++++++++++++++++++++---
 src/backend/utils/misc/guc.c        |  22 ++++-
 src/include/postmaster/postmaster.h |   7 ++
 20 files changed, 372 insertions(+), 40 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 95180b2ef5..21d77333be 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -103,6 +103,7 @@
 #include "lib/ilist.h"
 #include "libpq/auth.h"
 #include "libpq/libpq.h"
+#include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pg_getopt.h"
@@ -567,6 +568,77 @@ HANDLE		PostmasterHandle;
 #endif

 /*
+ * Initiate protocol negotiation phase;
+ * protocol negotiation is only supported for pgwire version 3.x, x>0.
+ *
+ * Ensure that the packet write buffer is flushed.
+ */
+int
+NegotiateServerProtocol(Port *port)
+{
+	if (whereToSendOutput != DestRemote ||
+		PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+		return -1;
+
+	int sendStatus = 0;
+
+	/* NegotiateServerProtocol packet structure
+	 *
+	 * [ 'Y' | msgLength | min_version | max_version | param_list_len | list of param names ]
+	 */
+
+	sendStatus = SendServerProtocolVersionMessage(port);
+
+	/* Ensure that the message buffer is flushed */
+	pq_flush();
+
+	return sendStatus;
+}
+
+/*
+ * Construct and send a ServerProtocolVersion message.
+ *
+ * Message contains:
+ * 1. minimum, maximum versions supported by the BE,
+ * 2. number of parameters that were honored by the BE from startup packet,
+ * 3. a list of strings consisting of the parameter names accepted by BE.
+ */
+int
+SendServerProtocolVersionMessage(Port *port)
+{
+	StringInfoData buf;
+
+	/* 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->guc_options) / 2, sizeof(int32));
+
+	ListCell *gucopts = list_head(port->guc_options);
+	while (gucopts)
+	{
+		char	   *name;
+
+		/* First comes key, which we need. */
+		name = lfirst(gucopts);
+		gucopts = lnext(gucopts);
+
+		/* Then comes value, which we don't need. */
+		gucopts = lnext(gucopts);
+
+		pq_sendstring(&buf, name);
+	}
+
+	pq_endmessage(&buf);
+
+	return 0;
+}
+
+/*
  * Postmaster main entry point
  */
 void
@@ -2050,20 +2122,6 @@ retry1:
 	 */
 	FrontendProtocol = proto;

-	/* Check we can handle the 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)))
-		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))));
-
 	/*
 	 * Now fetch parameters out of startup packet and save them into the Port
 	 * structure.  All data structures attached to the Port struct must be
@@ -2145,9 +2203,35 @@ retry1:
 			ereport(FATAL,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
 					 errmsg("invalid startup packet layout: expected terminator as last byte")));
+
+		/*
+		 * Need to negotiate pgwire protocol if
+		 * 1. FE version is not the same as BE version
+		 * 2. 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 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)))
+			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/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 246fea8693..69441f3f86 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3961,6 +3961,7 @@ static int	GUCNestLevel = 0;	/* 1 when in main transaction */

 static int	guc_var_compare(const void *a, const void *b);
 static int	guc_name_compare(const char *namea, const char *nameb);
+static bool is_optional(const char *guc_name);
 static void InitializeGUCOptionsFromEnvironment(void);
 static void InitializeOneGUCOption(struct config_generic *gconf);
 static void push_old_value(struct config_generic *gconf, GucAction action);
@@ -4416,7 +4417,7 @@ find_option(const char *name, bool create_placeholders, int elevel)
 		/*
 		 * Check if the name is qualified, and if so, add a placeholder.
 		 */
-		if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL)
+		if (strchr(name, GUC_QUALIFIER_SEPARATOR) != NULL || is_optional(name))
 			return add_placeholder_variable(name, elevel);
 	}

@@ -4467,6 +4468,25 @@ guc_name_compare(const char *namea, const char *nameb)
 	return 0;
 }

+/*
+ * A GUC variable is deemed optional if the name contains "_pq_" as a proper prefix.
+ *
+ * It takes the whole struct as input in case we want to move away from name-based
+ * tagging of optional variables.
+ */
+bool
+is_optional(const char *guc_name)
+{
+	const char *optionalPrefix = "_pq_";
+	bool isOptional = false;
+
+	/* "_pq_" must be a proper prefix of the guc name in all encodings */
+	if (guc_name_compare(guc_name, optionalPrefix) == 1 &&
+		strstr(guc_name, optionalPrefix))
+		isOptional = true;
+
+	return isOptional;
+}

 /*
  * Initialize GUC options during program startup.
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 0f85908b09..da0a3a79c3 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -13,6 +13,10 @@
 #ifndef _POSTMASTER_H
 #define _POSTMASTER_H

+#include "postgres.h"
+
+#include "libpq/libpq-be.h"
+
 /* GUC options */
 extern bool EnableSSL;
 extern int	ReservedBackends;
@@ -46,6 +50,9 @@ extern int	postmaster_alive_fds[2];

 extern const char *progname;

+extern int NegotiateServerProtocol(Port *port);
+extern int SendServerProtocolVersionMessage(Port *port);
+
 extern void PostmasterMain(int argc, char *argv[]) pg_attribute_noreturn();
 extern void ClosePostmasterPorts(bool am_syslogger);

--
2.13.2.windows.1

