From a5c43b886942e96ec5c745041f2d6a50c3205147 Mon Sep 17 00:00:00 2001
From: Joe Conway <mail@joeconway.com>
Date: Wed, 17 Feb 2016 09:12:06 -0800
Subject: [PATCH] Add new system view, pg_config

Move and refactor the underlying code for the pg_config client
application to src/common in support of sharing it with a new
system information SRF called pg_config() which makes the same
information available via SQL. Additionally wrap the SRF with a
new system view, as called pg_config.

Patch by me with extensive input and review by Michael Paquier
and additional review by Alvaro Herrera.
---
 doc/src/sgml/catalogs.sgml           |  52 ++++
 src/backend/catalog/system_views.sql |   6 +
 src/backend/utils/misc/Makefile      |   4 +-
 src/backend/utils/misc/pg_config.c   | 103 +++++++
 src/bin/pg_config/Makefile           |  14 -
 src/bin/pg_config/pg_config.c        | 429 +++------------------------
 src/common/Makefile                  |  19 +-
 src/common/config_info.c             | 206 +++++++++++++
 src/include/catalog/catversion.h     |   2 +-
 src/include/catalog/pg_proc.h        |   4 +
 src/include/common/config_info.h     |  21 ++
 src/include/port.h                   |   1 +
 src/include/utils/builtins.h         |   3 +
 src/port/path.c                      |  30 ++
 src/test/regress/expected/rules.out  |   3 +
 src/tools/msvc/Mkvcbuild.pm          |   4 +-
 16 files changed, 491 insertions(+), 410 deletions(-)
 create mode 100644 src/backend/utils/misc/pg_config.c
 create mode 100644 src/common/config_info.c
 create mode 100644 src/include/common/config_info.h

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 412c8450ba4..d77e99988ff 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7349,6 +7349,11 @@
       <entry>available versions of extensions</entry>
      </row>
 
+     <row>
+      <entry><link linkend="view-pg-config"><structname>pg_config</structname></link></entry>
+      <entry>compile-time configuration parameters</entry>
+     </row>
+
      <row>
       <entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry>
       <entry>open cursors</entry>
@@ -7609,6 +7614,53 @@
   </para>
  </sect1>
 
+ <sect1 id="view-pg-config">
+  <title><structname>pg_config</structname></title>
+
+  <indexterm zone="view-pg-config">
+   <primary>pg_config</primary>
+  </indexterm>
+
+  <para>
+   The view <structname>pg_config</structname> describes the
+   compile-time configuration parameters of the currently installed
+   version of <productname>PostgreSQL</>. It is intended, for example, to
+   be used by software packages that want to interface to
+   <productname>PostgreSQL</> to facilitate finding the required header
+   files and libraries. It provides the same basic information as the
+   <xref linkend="app-pgconfig"> <productname>PostgreSQL</> Client
+   Application.
+  </para>
+
+  <table>
+   <title><structname>pg_config</> Columns</title>
+   <tgroup cols="3">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>name</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry>The parameter name</entry>
+     </row>
+
+     <row>
+      <entry><structfield>setting</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry>The parameter value</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+ </sect1>
+
  <sect1 id="view-pg-cursors">
   <title><structname>pg_cursors</structname></title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe589d5e..abf9a7007c5 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -433,6 +433,12 @@ CREATE VIEW pg_timezone_abbrevs AS
 CREATE VIEW pg_timezone_names AS
     SELECT * FROM pg_timezone_names();
 
+CREATE VIEW pg_config AS
+    SELECT * FROM pg_config();
+
+REVOKE ALL on pg_config FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
+
 -- Statistics views
 
 CREATE VIEW pg_stat_all_tables AS
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 788910157de..a0c82c17438 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
-OBJS = guc.o help_config.o pg_rusage.o ps_status.o rls.o \
-       sampling.o superuser.o timeout.o tzparser.o
+OBJS = guc.o help_config.o pg_config.o pg_rusage.o \
+       ps_status.o rls.o sampling.o superuser.o timeout.o tzparser.o
 
 # This location might depend on the installation directories. Therefore
 # we can't subsitute it into pg_config.h.
diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
new file mode 100644
index 00000000000..3d6b9f27c08
--- /dev/null
+++ b/src/backend/utils/misc/pg_config.c
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_config.c
+ *		Expose same output as pg_config except as an SRF
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/misc/pg_config.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "catalog/pg_type.h"
+#include "common/config_info.h"
+#include "utils/builtins.h"
+#include "utils/elog.h"
+#include "port.h"
+
+Datum
+pg_config(PG_FUNCTION_ARGS)
+{
+	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	Tuplestorestate	   *tupstore;
+	HeapTuple			tuple;
+	TupleDesc			tupdesc;
+	AttInMetadata	   *attinmeta;
+	MemoryContext		per_query_ctx;
+	MemoryContext		oldcontext;
+	ConfigData		   *configdata;
+	size_t				configdata_len;
+	char			   *values[2];
+	int					i = 0;
+
+	/* check to see if caller supports us returning a tuplestore */
+	if (!rsinfo || !(rsinfo->allowedModes & SFRM_Materialize))
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("materialize mode required, but it is not "
+						"allowed in this context")));
+
+	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+	/* get the requested return tuple description */
+	tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+
+	/*
+	 * Check to make sure we have a reasonable tuple descriptor
+	 */
+	if (tupdesc->natts != 2 ||
+		tupdesc->attrs[0]->atttypid != TEXTOID ||
+		tupdesc->attrs[1]->atttypid != TEXTOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("query-specified return tuple and "
+						"function return type are not compatible")));
+
+	/* OK to use it */
+	attinmeta = TupleDescGetAttInMetadata(tupdesc);
+
+	/* let the caller know we're sending back a tuplestore */
+	rsinfo->returnMode = SFRM_Materialize;
+
+	/* initialize our tuplestore */
+	tupstore = tuplestore_begin_heap(true, false, work_mem);
+
+	configdata = get_configdata(my_exec_path, &configdata_len);
+	for (i = 0; i < configdata_len; i++)
+	{
+		values[0] = configdata[i].name;
+		values[1] = configdata[i].setting;
+
+		tuple = BuildTupleFromCStrings(attinmeta, values);
+		tuplestore_puttuple(tupstore, tuple);
+	}
+
+	/*
+	 * no longer need the tuple descriptor reference created by
+	 * TupleDescGetAttInMetadata()
+	 */
+	ReleaseTupleDesc(tupdesc);
+
+	tuplestore_donestoring(tupstore);
+	rsinfo->setResult = tupstore;
+
+	/*
+	 * SFRM_Materialize mode expects us to return a NULL Datum. The actual
+	 * tuples are in our tuplestore and passed back through
+	 * rsinfo->setResult. rsinfo->setDesc is set to the tuple description
+	 * that we actually used to build our tuples with, so the caller can
+	 * verify we did what it was expecting.
+	 */
+	rsinfo->setDesc = tupdesc;
+	MemoryContextSwitchTo(oldcontext);
+
+	return (Datum) 0;
+}
diff --git a/src/bin/pg_config/Makefile b/src/bin/pg_config/Makefile
index 812c4a1875c..26fbaadaddc 100644
--- a/src/bin/pg_config/Makefile
+++ b/src/bin/pg_config/Makefile
@@ -17,20 +17,6 @@ include $(top_builddir)/src/Makefile.global
 
 OBJS=   pg_config.o $(WIN32RES)
 
-# don't include subdirectory-path-dependent -I and -L switches
-STD_CPPFLAGS := $(filter-out -I$(top_srcdir)/src/include -I$(top_builddir)/src/include,$(CPPFLAGS))
-STD_LDFLAGS := $(filter-out -L$(top_builddir)/src/port,$(LDFLAGS))
-
-override CPPFLAGS += -DVAL_CONFIGURE="\"$(configure_args)\""
-override CPPFLAGS += -DVAL_CC="\"$(CC)\""
-override CPPFLAGS += -DVAL_CPPFLAGS="\"$(STD_CPPFLAGS)\""
-override CPPFLAGS += -DVAL_CFLAGS="\"$(CFLAGS)\""
-override CPPFLAGS += -DVAL_CFLAGS_SL="\"$(CFLAGS_SL)\""
-override CPPFLAGS += -DVAL_LDFLAGS="\"$(STD_LDFLAGS)\""
-override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\""
-override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
-override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
-
 all: pg_config
 
 pg_config: $(OBJS) | submake-libpgport
diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b1429405c9..23c04957be1 100644
--- a/src/bin/pg_config/pg_config.c
+++ b/src/bin/pg_config/pg_config.c
@@ -25,363 +25,9 @@
 #include "postgres_fe.h"
 
 #include "port.h"
+#include "common/config_info.h"
 
 static const char *progname;
-static char mypath[MAXPGPATH];
-
-
-/*
- * This function cleans up the paths for use with either cmd.exe or Msys
- * on Windows. We need them to use filenames without spaces, for which a
- * short filename is the safest equivalent, eg:
- *		C:/Progra~1/
- */
-static void
-cleanup_path(char *path)
-{
-#ifdef WIN32
-	char	   *ptr;
-
-	/*
-	 * GetShortPathName() will fail if the path does not exist, or short names
-	 * are disabled on this file system.  In both cases, we just return the
-	 * original path.  This is particularly useful for --sysconfdir, which
-	 * might not exist.
-	 */
-	GetShortPathName(path, path, MAXPGPATH - 1);
-
-	/* Replace '\' with '/' */
-	for (ptr = path; *ptr; ptr++)
-	{
-		if (*ptr == '\\')
-			*ptr = '/';
-	}
-#endif
-}
-
-
-/*
- * For each piece of information known to pg_config, we define a subroutine
- * to print it.  This is probably overkill, but it avoids code duplication
- * and accidentally omitting items from the "all" display.
- */
-
-static void
-show_bindir(bool all)
-{
-	char		path[MAXPGPATH];
-	char	   *lastsep;
-
-	if (all)
-		printf("BINDIR = ");
-	/* assume we are located in the bindir */
-	strcpy(path, mypath);
-	lastsep = strrchr(path, '/');
-
-	if (lastsep)
-		*lastsep = '\0';
-
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_docdir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("DOCDIR = ");
-	get_doc_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_htmldir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("HTMLDIR = ");
-	get_html_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_includedir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("INCLUDEDIR = ");
-	get_include_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_pkgincludedir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("PKGINCLUDEDIR = ");
-	get_pkginclude_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_includedir_server(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("INCLUDEDIR-SERVER = ");
-	get_includeserver_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_libdir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("LIBDIR = ");
-	get_lib_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_pkglibdir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("PKGLIBDIR = ");
-	get_pkglib_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_localedir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("LOCALEDIR = ");
-	get_locale_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_mandir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("MANDIR = ");
-	get_man_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_sharedir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("SHAREDIR = ");
-	get_share_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_sysconfdir(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("SYSCONFDIR = ");
-	get_etc_path(mypath, path);
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_pgxs(bool all)
-{
-	char		path[MAXPGPATH];
-
-	if (all)
-		printf("PGXS = ");
-	get_pkglib_path(mypath, path);
-	strlcat(path, "/pgxs/src/makefiles/pgxs.mk", sizeof(path));
-	cleanup_path(path);
-	printf("%s\n", path);
-}
-
-static void
-show_configure(bool all)
-{
-#ifdef VAL_CONFIGURE
-	if (all)
-		printf("CONFIGURE = ");
-	printf("%s\n", VAL_CONFIGURE);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_cc(bool all)
-{
-#ifdef VAL_CC
-	if (all)
-		printf("CC = ");
-	printf("%s\n", VAL_CC);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_cppflags(bool all)
-{
-#ifdef VAL_CPPFLAGS
-	if (all)
-		printf("CPPFLAGS = ");
-	printf("%s\n", VAL_CPPFLAGS);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_cflags(bool all)
-{
-#ifdef VAL_CFLAGS
-	if (all)
-		printf("CFLAGS = ");
-	printf("%s\n", VAL_CFLAGS);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_cflags_sl(bool all)
-{
-#ifdef VAL_CFLAGS_SL
-	if (all)
-		printf("CFLAGS_SL = ");
-	printf("%s\n", VAL_CFLAGS_SL);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_ldflags(bool all)
-{
-#ifdef VAL_LDFLAGS
-	if (all)
-		printf("LDFLAGS = ");
-	printf("%s\n", VAL_LDFLAGS);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_ldflags_ex(bool all)
-{
-#ifdef VAL_LDFLAGS_EX
-	if (all)
-		printf("LDFLAGS_EX = ");
-	printf("%s\n", VAL_LDFLAGS_EX);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_ldflags_sl(bool all)
-{
-#ifdef VAL_LDFLAGS_SL
-	if (all)
-		printf("LDFLAGS_SL = ");
-	printf("%s\n", VAL_LDFLAGS_SL);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_libs(bool all)
-{
-#ifdef VAL_LIBS
-	if (all)
-		printf("LIBS = ");
-	printf("%s\n", VAL_LIBS);
-#else
-	if (!all)
-	{
-		fprintf(stderr, _("not recorded\n"));
-		exit(1);
-	}
-#endif
-}
-
-static void
-show_version(bool all)
-{
-	if (all)
-		printf("VERSION = ");
-	printf("PostgreSQL " PG_VERSION "\n");
-}
-
 
 /*
  * Table of known information items
@@ -391,33 +37,33 @@ show_version(bool all)
 typedef struct
 {
 	const char *switchname;
-	void		(*show_func) (bool all);
+	const char *configname;
 } InfoItem;
 
 static const InfoItem info_items[] = {
-	{"--bindir", show_bindir},
-	{"--docdir", show_docdir},
-	{"--htmldir", show_htmldir},
-	{"--includedir", show_includedir},
-	{"--pkgincludedir", show_pkgincludedir},
-	{"--includedir-server", show_includedir_server},
-	{"--libdir", show_libdir},
-	{"--pkglibdir", show_pkglibdir},
-	{"--localedir", show_localedir},
-	{"--mandir", show_mandir},
-	{"--sharedir", show_sharedir},
-	{"--sysconfdir", show_sysconfdir},
-	{"--pgxs", show_pgxs},
-	{"--configure", show_configure},
-	{"--cc", show_cc},
-	{"--cppflags", show_cppflags},
-	{"--cflags", show_cflags},
-	{"--cflags_sl", show_cflags_sl},
-	{"--ldflags", show_ldflags},
-	{"--ldflags_ex", show_ldflags_ex},
-	{"--ldflags_sl", show_ldflags_sl},
-	{"--libs", show_libs},
-	{"--version", show_version},
+	{"--bindir", "BINDIR"},
+	{"--docdir", "DOCDIR"},
+	{"--htmldir", "HTMLDIR"},
+	{"--includedir", "INCLUDEDIR"},
+	{"--pkgincludedir", "PKGINCLUDEDIR"},
+	{"--includedir-server", "INCLUDEDIR-SERVER"},
+	{"--libdir", "LIBDIR"},
+	{"--pkglibdir", "PKGLIBDIR"},
+	{"--localedir", "LOCALEDIR"},
+	{"--mandir", "MANDIR"},
+	{"--sharedir", "SHAREDIR"},
+	{"--sysconfdir", "SYSCONFDIR"},
+	{"--pgxs", "PGXS"},
+	{"--configure", "CONFIGURE"},
+	{"--cc", "CC"},
+	{"--cppflags", "CPPFLAGS"},
+	{"--cflags", "CFLAGS"},
+	{"--cflags_sl", "CFLAGS_SL"},
+	{"--ldflags", "LDFLAGS"},
+	{"--ldflags_ex", "LDFLAGS_EX"},
+	{"--ldflags_sl", "LDFLAGS_SL"},
+	{"--libs", "LIBS"},
+	{"--version", "VERSION"},
 	{NULL, NULL}
 };
 
@@ -466,22 +112,27 @@ advice(void)
 }
 
 static void
-show_all(void)
+show_item(const char *configname,
+		  ConfigData *configdata,
+		  size_t configdata_len)
 {
 	int			i;
 
-	for (i = 0; info_items[i].switchname != NULL; i++)
+	for (i = 0; i < configdata_len; i++)
 	{
-		(*info_items[i].show_func) (true);
+		if (strcmp(configname, configdata[i].name) == 0)
+			printf("%s = %s\n", configdata[i].name, configdata[i].setting);
 	}
 }
 
 int
 main(int argc, char **argv)
 {
+	ConfigData *configdata;
+	size_t		configdata_len;
+	char		my_exec_path[MAXPGPATH];
 	int			i;
 	int			j;
-	int			ret;
 
 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
 
@@ -497,28 +148,30 @@ main(int argc, char **argv)
 		}
 	}
 
-	ret = find_my_exec(argv[0], mypath);
-
-	if (ret)
+	if (find_my_exec(argv[0], my_exec_path) < 0)
 	{
 		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
 		exit(1);
 	}
 
+	configdata = get_configdata(my_exec_path, &configdata_len);
 	/* no arguments -> print everything */
 	if (argc < 2)
 	{
-		show_all();
+		for (i = 0; i < configdata_len; i++)
+			printf("%s = %s\n", configdata[i].name, configdata[i].setting);
 		exit(0);
 	}
 
+	/* otherwise print requested items */
 	for (i = 1; i < argc; i++)
 	{
 		for (j = 0; info_items[j].switchname != NULL; j++)
 		{
 			if (strcmp(argv[i], info_items[j].switchname) == 0)
 			{
-				(*info_items[j].show_func) (false);
+				show_item(info_items[j].configname,
+						  configdata, configdata_len);
 				break;
 			}
 		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e768b..bde4fc25975 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -23,8 +23,21 @@ include $(top_builddir)/src/Makefile.global
 override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
 LIBS += $(PTHREAD_LIBS)
 
-OBJS_COMMON = exec.o pg_lzcompress.o pgfnames.o psprintf.o relpath.o \
-	rmtree.o string.o username.o wait_error.o
+# don't include subdirectory-path-dependent -I and -L switches
+STD_CPPFLAGS := $(filter-out -I$(top_srcdir)/src/include -I$(top_builddir)/src/include,$(CPPFLAGS))
+STD_LDFLAGS := $(filter-out -L$(top_builddir)/src/port,$(LDFLAGS))
+override CPPFLAGS += -DVAL_CONFIGURE="\"$(configure_args)\""
+override CPPFLAGS += -DVAL_CC="\"$(CC)\""
+override CPPFLAGS += -DVAL_CPPFLAGS="\"$(STD_CPPFLAGS)\""
+override CPPFLAGS += -DVAL_CFLAGS="\"$(CFLAGS)\""
+override CPPFLAGS += -DVAL_CFLAGS_SL="\"$(CFLAGS_SL)\""
+override CPPFLAGS += -DVAL_LDFLAGS="\"$(STD_LDFLAGS)\""
+override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\""
+override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
+override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
+
+OBJS_COMMON = config_info.o exec.o pg_lzcompress.o pgfnames.o psprintf.o \
+	relpath.o rmtree.o string.o username.o wait_error.o
 
 OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
 
@@ -61,7 +74,7 @@ libpgcommon_srv.a: $(OBJS_SRV)
 # a hack that might fail someday if there is a *_srv.o without a
 # corresponding *.o, but it works for now.
 %_srv.o: %.c %.o
-	$(CC) $(CFLAGS) $(subst -DFRONTEND,, $(CPPFLAGS)) -c $< -o $@
+	$(CC) $(CFLAGS) $(subst -DFRONTEND ,, $(CPPFLAGS)) -c $< -o $@
 
 $(OBJS_SRV): | submake-errcodes
 
diff --git a/src/common/config_info.c b/src/common/config_info.c
new file mode 100644
index 00000000000..9053a8cbe48
--- /dev/null
+++ b/src/common/config_info.c
@@ -0,0 +1,206 @@
+/*-------------------------------------------------------------------------
+ *
+ * config_info.c
+ *		Common code for pg_config output
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/common/config_info.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#endif
+
+#include "miscadmin.h"
+#include "common/config_info.h"
+
+static size_t configdata_names_len(void);
+
+static const char *const configdata_names[] =
+{
+	"BINDIR",
+	"DOCDIR",
+	"HTMLDIR",
+	"INCLUDEDIR",
+	"PKGINCLUDEDIR",
+	"INCLUDEDIR-SERVER",
+	"LIBDIR",
+	"PKGLIBDIR",
+	"LOCALEDIR",
+	"MANDIR",
+	"SHAREDIR",
+	"SYSCONFDIR",
+	"PGXS",
+	"CONFIGURE",
+	"CC",
+	"CPPFLAGS",
+	"CFLAGS",
+	"CFLAGS_SL",
+	"LDFLAGS",
+	"LDFLAGS_EX",
+	"LDFLAGS_SL",
+	"LIBS",
+	"VERSION",
+	NULL
+};
+
+static size_t
+configdata_names_len(void)
+{
+	size_t	i = 0;
+
+	while (configdata_names[i])
+		i++;
+
+	return i;
+}
+
+/*
+ * get_configdata(char *my_exec_path, size_t *configdata_len)
+ *
+ * Get configure-time constants. The caller is responsible
+ * for pfreeing the result.
+ */
+ConfigData *
+get_configdata(char *my_exec_path, size_t *configdata_len)
+{
+	ConfigData	   *configdata;
+	char			path[MAXPGPATH];
+	char		   *lastsep;
+	int				i;
+
+	*configdata_len = configdata_names_len();
+	configdata = palloc(*configdata_len * sizeof(ConfigData));
+
+	/*
+	 * initialize configdata names
+	 *
+	 * These better be in sync with the settings manually
+	 * defined below.
+	 */
+	for (i = 0; i < *configdata_len; i++)
+		configdata[i].name = pstrdup(configdata_names[i]);
+
+	strcpy(path, my_exec_path);
+	lastsep = strrchr(path, '/');
+	if (lastsep)
+		*lastsep = '\0';
+	cleanup_path(path);
+	configdata[0].setting = pstrdup(path);
+
+	get_doc_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[1].setting = pstrdup(path);
+
+	get_html_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[2].setting = pstrdup(path);
+
+	get_include_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[3].setting = pstrdup(path);
+
+	get_pkginclude_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[4].setting = pstrdup(path);
+
+	get_includeserver_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[5].setting = pstrdup(path);
+
+	get_lib_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[6].setting = pstrdup(path);
+
+	get_pkglib_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[7].setting = pstrdup(path);
+
+	get_locale_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[8].setting = pstrdup(path);
+
+	get_man_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[9].setting = pstrdup(path);
+
+	get_share_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[10].setting = pstrdup(path);
+
+	get_etc_path(my_exec_path, path);
+	cleanup_path(path);
+	configdata[11].setting = pstrdup(path);
+
+	get_pkglib_path(my_exec_path, path);
+	strlcat(path, "/pgxs/src/makefiles/pgxs.mk", sizeof(path));
+	cleanup_path(path);
+	configdata[12].setting = pstrdup(path);
+
+#ifdef VAL_CONFIGURE
+	configdata[13].setting = pstrdup(VAL_CONFIGURE);
+#else
+	configdata[13].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_CC
+	configdata[14].setting = pstrdup(VAL_CC);
+#else
+	configdata[14].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_CPPFLAGS
+	configdata[15].setting = pstrdup(VAL_CPPFLAGS);
+#else
+	configdata[15].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_CFLAGS
+	configdata[16].setting = pstrdup(VAL_CFLAGS);
+#else
+	configdata[16].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_CFLAGS_SL
+	configdata[17].setting = pstrdup(VAL_CFLAGS_SL);
+#else
+	configdata[17].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_LDFLAGS
+	configdata[18].setting = pstrdup(VAL_LDFLAGS);
+#else
+	configdata[18].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_LDFLAGS_EX
+	configdata[19].setting = pstrdup(VAL_LDFLAGS_EX);
+#else
+	configdata[19].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_LDFLAGS_SL
+	configdata[20].setting = pstrdup(VAL_LDFLAGS_SL);
+#else
+	configdata[20].setting = pstrdup(_("not recorded"));
+#endif
+
+#ifdef VAL_LIBS
+	configdata[21].setting = pstrdup(VAL_LIBS);
+#else
+	configdata[21].setting = pstrdup(_("not recorded"));
+#endif
+
+	configdata[22].setting = pstrdup("PostgreSQL " PG_VERSION);
+
+	return configdata;
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 378c40f44d3..b4131f9eb45 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201602071
+#define CATALOG_VERSION_NO	201602171
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b24e434f2c7..2222e8fdf13 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5208,6 +5208,10 @@ DESCR("row security for current context active on table by table oid");
 DATA(insert OID = 3299 (  row_security_active	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 16 "25" _null_ _null_ _null_ _null_ _null_	row_security_active_name _null_ _null_ _null_ ));
 DESCR("row security for current context active on table by table name");
 
+/* pg_config */
+DATA(insert OID = 3400 ( pg_config PGNSP PGUID 12 1 23 0 0 f f f f t t i r 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_config _null_ _null_ _null_ ));
+DESCR("pg_config binary as a function");
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/common/config_info.h b/src/include/common/config_info.h
new file mode 100644
index 00000000000..649ef5cf4be
--- /dev/null
+++ b/src/include/common/config_info.h
@@ -0,0 +1,21 @@
+/*
+ * config_info.h
+ *		Common code for pg_config output
+ *
+ *	Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ *	src/include/common/config_info.h
+ */
+#ifndef COMMON_CONFIG_INFO_H
+#define COMMON_CONFIG_INFO_H
+
+typedef struct ConfigData
+{
+	char	   *name;
+	char	   *setting;
+} ConfigData;
+
+extern ConfigData *get_configdata(char *my_exec_path,
+								  size_t *configdata_len);
+
+#endif   /* COMMON_CONFIG_INFO_H */
diff --git a/src/include/port.h b/src/include/port.h
index 9fc79f413ea..cb13dd80c73 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -42,6 +42,7 @@ extern void join_path_components(char *ret_path,
 					 const char *head, const char *tail);
 extern void canonicalize_path(char *path);
 extern void make_native_path(char *path);
+extern void cleanup_path(char *path);
 extern bool path_contains_parent_reference(const char *path);
 extern bool path_is_relative_and_below_cwd(const char *path);
 extern bool path_is_prefix_of_path(const char *path1, const char *path2);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index affcc01a409..a784de9d287 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1147,6 +1147,9 @@ extern Datum set_config_by_name(PG_FUNCTION_ARGS);
 extern Datum show_all_settings(PG_FUNCTION_ARGS);
 extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
 
+/* pg_config.c */
+extern Datum pg_config(PG_FUNCTION_ARGS);
+
 /* rls.c */
 extern Datum row_security_active(PG_FUNCTION_ARGS);
 extern Datum row_security_active_name(PG_FUNCTION_ARGS);
diff --git a/src/port/path.c b/src/port/path.c
index a418f93896a..5c9de0cd331 100644
--- a/src/port/path.c
+++ b/src/port/path.c
@@ -171,6 +171,36 @@ make_native_path(char *filename)
 }
 
 
+/*
+ * This function cleans up the paths for use with either cmd.exe or Msys
+ * on Windows. We need them to use filenames without spaces, for which a
+ * short filename is the safest equivalent, eg:
+ *		C:/Progra~1/
+ */
+void
+cleanup_path(char *path)
+{
+#ifdef WIN32
+	char	   *ptr;
+
+	/*
+	 * GetShortPathName() will fail if the path does not exist, or short names
+	 * are disabled on this file system.  In both cases, we just return the
+	 * original path.  This is particularly useful for --sysconfdir, which
+	 * might not exist.
+	 */
+	GetShortPathName(path, path, MAXPGPATH - 1);
+
+	/* Replace '\' with '/' */
+	for (ptr = path; *ptr; ptr++)
+	{
+		if (*ptr == '\\')
+			*ptr = '/';
+	}
+#endif
+}
+
+
 /*
  * join_path_components - join two path components, inserting a slash
  *
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2bdba2d1a18..81bc5c9504b 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1305,6 +1305,9 @@ pg_available_extensions| SELECT e.name,
     e.comment
    FROM (pg_available_extensions() e(name, default_version, comment)
      LEFT JOIN pg_extension x ON ((e.name = x.extname)));
+pg_config| SELECT pg_config.name,
+    pg_config.setting
+   FROM pg_config() pg_config(name, setting);
 pg_cursors| SELECT c.name,
     c.statement,
     c.is_holdable,
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 1dba7d9662e..e4fb44e6398 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -106,8 +106,8 @@ sub mkvcbuild
 	}
 
 	our @pgcommonallfiles = qw(
-	  exec.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
-	  string.c username.c wait_error.c);
+	  config_info.c exec.c pg_lzcompress.c pgfnames.c psprintf.c
+	  relpath.c rmtree.c string.c username.c wait_error.c);
 
 	our @pgcommonfrontendfiles = (
 		@pgcommonallfiles, qw(fe_memutils.c
-- 
GitLab