From 5d23609b9f72373e2de1e15ad77bf57fd7812742 Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Thu, 19 Jan 2017 10:45:42 -0500
Subject: [PATCH 2/2] Add a SHOW command to the walsender command language.

The walsender process doesn't have catalog access, so this requires a
few hacks, but they're not too ugly.

Original patch by Beena Emerson, revised significantly by me.
---
 doc/src/sgml/protocol.sgml             | 24 +++++++++++
 src/backend/access/common/tupdesc.c    | 79 ++++++++++++++++++++++++++++++++++
 src/backend/replication/repl_gram.y    | 22 +++++++++-
 src/backend/replication/repl_scanner.l |  1 +
 src/backend/replication/walsender.c    | 12 ++++++
 src/backend/utils/misc/guc.c           | 16 +++----
 src/include/access/tupdesc.h           |  7 +++
 7 files changed, 151 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 9ba147c..3211f6d 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1394,6 +1394,30 @@ The commands accepted in walsender mode are:
   </varlistentry>
 
   <varlistentry>
+    <term><literal>SHOW</literal> <replaceable class="parameter">name</replaceable>
+     <indexterm><primary>SHOW</primary></indexterm>
+    </term>
+    <listitem>
+     <para>
+      Requests the server to send the current setting of a run-time parameter.
+      This is similar to the SQL command <xref linkend="sql-show">.
+     </para>
+
+     <variablelist>
+      <varlistentry>
+       <term><replaceable class="parameter">name</></term>
+       <listitem>
+         <para>
+          The name of a run-time parameter. Available parameters are documented
+          in <xref linkend="runtime-config">.
+         </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </listitem>
+  </varlistentry>
+
+  <varlistentry>
     <term><literal>TIMELINE_HISTORY</literal> <replaceable class="parameter">tli</replaceable>
      <indexterm><primary>TIMELINE_HISTORY</primary></indexterm>
     </term>
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 54a32c0..083c030 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -20,6 +20,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
 #include "parser/parse_type.h"
@@ -554,6 +555,84 @@ TupleDescInitEntry(TupleDesc desc,
 }
 
 /*
+ * TupleDescInitBuiltinEntry
+ *		Initialize a tuple descriptor without catalog access.  Only
+ *		a limited range of builtin types are supported.
+ */
+void
+TupleDescInitBuiltinEntry(TupleDesc desc,
+						  AttrNumber attributeNumber,
+						  const char *attributeName,
+						  Oid oidtypeid,
+						  int32 typmod,
+						  int attdim)
+{
+	Form_pg_attribute att;
+
+	/* sanity checks */
+	AssertArg(PointerIsValid(desc));
+	AssertArg(attributeNumber >= 1);
+	AssertArg(attributeNumber <= desc->natts);
+
+	/* initialize the attribute fields */
+	att = desc->attrs[attributeNumber - 1];
+	att->attrelid = 0;			/* dummy value */
+
+	/* unlike TupleDescInitEntry, we require an attribute name */
+	Assert(attributeName != NULL);
+	namestrcpy(&(att->attname), attributeName);
+
+	att->attstattarget = -1;
+	att->attcacheoff = -1;
+	att->atttypmod = typmod;
+
+	att->attnum = attributeNumber;
+	att->attndims = attdim;
+
+	att->attnotnull = false;
+	att->atthasdef = false;
+	att->attisdropped = false;
+	att->attislocal = true;
+	att->attinhcount = 0;
+	/* attacl, attoptions and attfdwoptions are not present in tupledescs */
+
+	att->atttypid = oidtypeid;
+
+	/*
+	 * Our goal here is to support just enough types to let basic builtin
+	 * commands work without catalog access - e.g. so that we can do certain
+	 * things even in processes that are not connected to a database.
+	 */
+	switch (oidtypeid)
+	{
+		case TEXTOID:
+		case TEXTARRAYOID:
+			att->attlen = -1;
+			att->attbyval = false;
+			att->attalign = 'i';
+			att->attstorage = 'x';
+			att->attcollation = DEFAULT_COLLATION_OID;
+			break;
+
+		case BOOLOID:
+			att->attlen = 1;
+			att->attbyval = true;
+			att->attalign = 'c';
+			att->attstorage = 'p';
+			att->attcollation = InvalidOid;
+			break;
+
+		case INT4OID:
+			att->attlen = 4;
+			att->attbyval = true;
+			att->attalign = 'i';
+			att->attstorage = 'p';
+			att->attcollation = InvalidOid;
+			break;
+	}
+}
+
+/*
  * TupleDescInitEntryCollation
  *
  * Assign a nondefault collation to a previously initialized tuple descriptor
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index d962c76..b35d0f0 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -61,6 +61,7 @@ Node *replication_parse_result;
 /* Keyword tokens. */
 %token K_BASE_BACKUP
 %token K_IDENTIFY_SYSTEM
+%token K_SHOW
 %token K_START_REPLICATION
 %token K_CREATE_REPLICATION_SLOT
 %token K_DROP_REPLICATION_SLOT
@@ -82,14 +83,14 @@ Node *replication_parse_result;
 %type <node>	command
 %type <node>	base_backup start_replication start_logical_replication
 				create_replication_slot drop_replication_slot identify_system
-				timeline_history
+				timeline_history show
 %type <list>	base_backup_opt_list
 %type <defelt>	base_backup_opt
 %type <uintval>	opt_timeline
 %type <list>	plugin_options plugin_opt_list
 %type <defelt>	plugin_opt_elem
 %type <node>	plugin_opt_arg
-%type <str>		opt_slot
+%type <str>		opt_slot var_name
 %type <boolval>	opt_reserve_wal opt_temporary
 
 %%
@@ -112,6 +113,7 @@ command:
 			| create_replication_slot
 			| drop_replication_slot
 			| timeline_history
+			| show
 			;
 
 /*
@@ -125,6 +127,22 @@ identify_system:
 			;
 
 /*
+ * SHOW setting
+ */
+show:
+			K_SHOW var_name
+				{
+					VariableShowStmt *n = makeNode(VariableShowStmt);
+					n->name = $2;
+					$$ = (Node *) n;
+				}
+
+var_name:	IDENT	{ $$ = $1; }
+			| var_name '.' IDENT
+				{ $$ = psprintf("%s.%s", $1, $3); }
+		;
+
+/*
  * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT]
  * [MAX_RATE %d] [TABLESPACE_MAP]
  */
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index a3b5f92..37f8579 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -83,6 +83,7 @@ identifier		{ident_start}{ident_cont}*
 BASE_BACKUP			{ return K_BASE_BACKUP; }
 FAST			{ return K_FAST; }
 IDENTIFY_SYSTEM		{ return K_IDENTIFY_SYSTEM; }
+SHOW		{ return K_SHOW; }
 LABEL			{ return K_LABEL; }
 NOWAIT			{ return K_NOWAIT; }
 PROGRESS			{ return K_PROGRESS; }
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index f3082c3..5909b7d 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -43,6 +43,7 @@
 #include <signal.h>
 #include <unistd.h>
 
+#include "access/printtup.h"
 #include "access/timeline.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -72,11 +73,13 @@
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "storage/procarray.h"
+#include "tcop/dest.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/pg_lsn.h"
+#include "utils/portal.h"
 #include "utils/ps_status.h"
 #include "utils/resowner.h"
 #include "utils/timeout.h"
@@ -1365,6 +1368,15 @@ exec_replication_command(const char *cmd_string)
 			SendTimeLineHistory((TimeLineHistoryCmd *) cmd_node);
 			break;
 
+		case T_VariableShowStmt:
+			{
+				DestReceiver *dest = CreateDestReceiver(DestRemoteSimple);
+				VariableShowStmt *n = (VariableShowStmt *) cmd_node;
+
+				GetPGVariable(n->name, dest);
+			}
+			break;
+
 		default:
 			elog(ERROR, "unrecognized replication command node tag: %u",
 				 cmd_node->type);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 4e2bd4c..3b238aa 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -7864,8 +7864,8 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
 
 	/* need a tuple descriptor representing a single TEXT column */
 	tupdesc = CreateTemplateTupleDesc(1, false);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
-					   TEXTOID, -1, 0);
+	TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, varname,
+							  TEXTOID, -1, 0);
 
 	/* prepare for projection of tuples */
 	tstate = begin_tup_output_tupdesc(dest, tupdesc);
@@ -7891,12 +7891,12 @@ ShowAllGUCConfig(DestReceiver *dest)
 
 	/* need a tuple descriptor representing three TEXT columns */
 	tupdesc = CreateTemplateTupleDesc(3, false);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
-					   TEXTOID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
-					   TEXTOID, -1, 0);
-	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "description",
-					   TEXTOID, -1, 0);
+	TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 1, "name",
+							  TEXTOID, -1, 0);
+	TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 2, "setting",
+							  TEXTOID, -1, 0);
+	TupleDescInitBuiltinEntry(tupdesc, (AttrNumber) 3, "description",
+							  TEXTOID, -1, 0);
 
 	/* prepare for projection of tuples */
 	tstate = begin_tup_output_tupdesc(dest, tupdesc);
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 0012877..b48f839 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -119,6 +119,13 @@ extern void TupleDescInitEntry(TupleDesc desc,
 				   int32 typmod,
 				   int attdim);
 
+extern void TupleDescInitBuiltinEntry(TupleDesc desc,
+						  AttrNumber attributeNumber,
+						  const char *attributeName,
+						  Oid oidtypeid,
+						  int32 typmod,
+						  int attdim);
+
 extern void TupleDescInitEntryCollation(TupleDesc desc,
 							AttrNumber attributeNumber,
 							Oid collationid);
-- 
2.5.4 (Apple Git-61)

