From 85de9f48f80a3bfd9e8bdd4f1ba6b177b1ff9749 Mon Sep 17 00:00:00 2001
From: Dave Cramer <davecramer@gmail.com>
Date: Thu, 11 Jul 2019 08:20:14 -0400
Subject: [PATCH] Add a STARTUP packet option to set GUC_REPORT on GUC's that
 currently do not have that option set. There is a facility to add protocol
 options using _pq_.<newoption> The new option name is report and takes a
 comma delmited string of GUC names which will have GUC_REPORT set. Add
 functionality into libpq to accept this new option key

---
 src/backend/postmaster/postmaster.c | 36 +++++++++++++++++++++++------
 src/backend/utils/init/postinit.c   | 13 +++++++++++
 src/backend/utils/misc/guc.c        | 22 ++++++++++++++++++
 src/include/libpq/libpq-be.h        |  1 +
 src/include/utils/guc.h             |  1 +
 src/interfaces/libpq/fe-connect.c   |  5 +++-
 src/interfaces/libpq/fe-protocol3.c |  3 +++
 src/interfaces/libpq/libpq-int.h    |  2 ++
 8 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index eb9e0221f8..3e6cb8769b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2094,6 +2094,7 @@ retry1:
 		 * zeroing extra byte above.
 		 */
 		port->guc_options = NIL;
+		port->guc_report = NIL;
 
 		while (offset < len)
 		{
@@ -2138,13 +2139,34 @@ retry1:
 			}
 			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));
+				if (strncasecmp(nameptr + 5, "report", 6) == 0)
+				{
+					char sep[3] = " ,";
+
+					/* avoid scribbling on valptr */
+					char *temp_val = pstrdup(valptr);
+
+					/* strtok is going to scribble on temp_val */
+					char *freeptr = temp_val;
+					char *guc_report = strtok(temp_val, sep);
+					while (guc_report)
+					{
+						port->guc_report = lappend(port->guc_report,
+												   pstrdup(guc_report));
+						guc_report = strtok(NULL, sep);
+					}
+					pfree(freeptr);
+				}
+				else
+				{
+					/*
+					 * 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
 			{
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 29c5ec7b58..98b9517143 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -1090,6 +1090,9 @@ process_startup_options(Port *port, bool am_superuser)
 {
 	GucContext	gucctx;
 	ListCell   *gucopts;
+	ListCell   *gucreport;
+	List 	   *gucReports;
+
 
 	gucctx = am_superuser ? PGC_SU_BACKEND : PGC_BACKEND;
 
@@ -1124,6 +1127,16 @@ process_startup_options(Port *port, bool am_superuser)
 		(void) process_postgres_switches(ac, av, gucctx, NULL);
 	}
 
+	/* Loop through the user requested GUC_REPORT and set them */
+	gucReports = port->guc_report;
+	foreach(gucreport,gucReports)
+	{
+		char		*name;
+
+		name = lfirst(gucreport);
+		SetConfigReport(name, true);
+	}
+
 	/*
 	 * Process any additional GUC variable settings passed in startup packet.
 	 * These are handled exactly like command-line variables.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 2178e1cf5e..25cf17f2a8 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -7630,7 +7630,29 @@ GetConfigOptionFlags(const char *name, bool missing_ok)
 	return record->flags;
 }
 
+/*
+ * Set the option to be GUC_REPORT
+ */
+
+bool
+SetConfigReport(const char *name, bool missing_ok)
+{
+	struct config_generic *record;
 
+	record = find_option(name, false, WARNING);
+	if (record == NULL)
+	{
+		if (missing_ok)
+			return 0;
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("unrecognized configuration parameter \"%s\"",
+						name)));
+	}
+	record->flags |= GUC_REPORT;
+
+	return 0;
+}
 /*
  * flatten_set_variable_args
  *		Given a parsenode List as emitted by the grammar for SET,
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 541f970f99..0350b70f02 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -141,6 +141,7 @@ typedef struct Port
 	char	   *user_name;
 	char	   *cmdline_options;
 	List	   *guc_options;
+	List	   *guc_report;
 
 	/*
 	 * The startup packet application name, only used here for the "connection
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 6791e0cbc2..8c4fc5ed62 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -348,6 +348,7 @@ extern const char *GetConfigOption(const char *name, bool missing_ok,
 								   bool restrict_privileged);
 extern const char *GetConfigOptionResetString(const char *name);
 extern int	GetConfigOptionFlags(const char *name, bool missing_ok);
+extern bool SetConfigReport(const char *name, bool missing_ok);
 extern void ProcessConfigFile(GucContext context);
 extern void InitializeGUCOptions(void);
 extern bool SelectConfigFiles(const char *userDoption, const char *progname);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f91f0f2efe..c0f59f37f8 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -353,6 +353,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
 	offsetof(struct pg_conn, target_session_attrs)},
 
+	{"_pq_.report",NULL, NULL, NULL,
+		"Report", "D", 5,
+		offsetof(struct pg_conn, guc_report)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
@@ -5560,7 +5564,6 @@ conninfo_array_parse(const char *const *keywords, const char *const *values,
 				PQconninfoFree(dbname_options);
 				return NULL;
 			}
-
 			/*
 			 * If we are on the first dbname parameter, and we have a parsed
 			 * connection string, copy those parameters across, overriding any
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index b04f7ec123..f48200b972 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2184,6 +2184,9 @@ build_startup_packet(const PGconn *conn, char *packet,
 		ADD_STARTUP_OPTION("replication", conn->replication);
 	if (conn->pgoptions && conn->pgoptions[0])
 		ADD_STARTUP_OPTION("options", conn->pgoptions);
+	if (conn->guc_report && conn->guc_report[0])
+		ADD_STARTUP_OPTION("_pq_.report", conn->guc_report);
+
 	if (conn->send_appname)
 	{
 		/* Use appname if present, otherwise use fallback */
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 64468ab4da..9ec8e8609b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -370,6 +370,8 @@ struct pg_conn
 	/* Type of connection to make.  Possible values: any, read-write. */
 	char	   *target_session_attrs;
 
+	char	   *guc_report;
+
 	/* Optional file to write trace info to */
 	FILE	   *Pfdebug;
 
-- 
2.20.1 (Apple Git-117)

