exposing pg_controldata and pg_config as functions

Started by Andrew Dunstanover 10 years ago106 messages
#1Andrew Dunstan
andrew@dunslane.net

Is there any significant interest in either of these?

Josh Berkus tells me that he would like pg_controldata information, and
I was a bit interested in pg_config information, for this reason: I had
a report of someone who had configured using --with-libxml but the xml
tests actually returned the results that are expected without xml being
configured. The regression tests thus passed, but should not have. It
occurred to me that if we had a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#2Joe Conway
mail@joeconway.com
In reply to: Andrew Dunstan (#1)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/20/2015 06:59 AM, Andrew Dunstan wrote:

Is there any significant interest in either of these?

Josh Berkus tells me that he would like pg_controldata information,
and I was a bit interested in pg_config information, for this
reason: I had a report of someone who had configured using
--with-libxml but the xml tests actually returned the results that
are expected without xml being configured. The regression tests
thus passed, but should not have. It occurred to me that if we had
a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

I've found use for them both in the past. A fair amount of bit-rot has
set it no doubt, and these were quick and dirty to begin with, but I
have these hacks from a while back:

https://github.com/jconway/pg_config
https://github.com/jconway/pg_controldata

- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV1epAAAoJEDfy90M199hlDjkP/AjAMtF4Q4rwfy5CsqA1rCgX
E/hLoTxExwU11nS2Q6IxC1unsXCDQrr2lkutKsw5Ybo78O7pMvR39GqShQ6ItaOr
xLxkYmxU1pEC4MAzZ6TR7RCAiP5SOgDEC3X6ArBqc0zub6FrnuYI3zv8eIgcTWqT
cFan4vCHYZnWUp3KQ0sOpSl4I/5jeW3AwrfTlnwskC8NwpP0Oa0DiXXTtXoRUYZI
CaWUsV9FgfzGvhyQCJpwcldEU9TprU24U09CpIVzSmw6Q9eQBHO4k+nT/Xw3BRjH
LxPM7gH9LweQOJhzP3J8agrJqbnSntayPZKG9ZsqMvC/8Ly+mlIO/X4cuEYpKO94
De9jO+aly6NhUdCpOdM6cZdqsTggXExaafl61wazYBUcLWotBnL9I1E9n59fm+yu
wgec7vdWIzZn6FYr+Ox2sgOBbxXVs3l/FLTCkoUgsZWRvlEL/naePr5TxMJMyqpo
pt15r1WRd4KwDEN4qxYHrzOab/T7QG1RS9Qr2v0GMPvQp4lIXgCq2aJJXy+iCDPE
lifDpHipk39h0r0RN377UFmfW3Z8DNLj0UQpuw+bOXtFLZpcA4WQdg64qXBaUU26
7icScC7+PpEr+HialFyA8lbDb9EVRrUaJ6CJajrGy8iuH/vpME2+40sgFTvavZtk
a0mfnPIdWJjzkldZGZ23
=hbBg
-----END PGP SIGNATURE-----

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Joe Conway
mail@joeconway.com
In reply to: Joe Conway (#2)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/20/2015 07:54 AM, Joe Conway wrote:

On 08/20/2015 06:59 AM, Andrew Dunstan wrote:

I was a bit interested in pg_config information, for this reason:
I had a report of someone who had configured using --with-libxml
but the xml tests actually returned the results that are expected
without xml being configured. The regression tests thus passed,
but should not have. It occurred to me that if we had a test
like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

I've found use for them both in the past. A fair amount of bit-rot
has set it no doubt, and these were quick and dirty to begin with,
but I have these hacks from a while back:

https://github.com/jconway/pg_config

The attached implements pg_config as a backend SRF and a matching
system view. A few notes:

1) The syntax is a bit different than what Andrew proposed:

8<----------------
select setting ~ '--with-libxml' as has_xml
from pg_config
where name = 'CONFIGURE';
has_xml
- ---------
t
(1 row)
8<----------------

In particular note that the name values are all upper case to be
consistent with pg_config, and at least currently there is no version
of the function which accepts a name as an argument (didn't seem
worthwhile to me).

2) No docs or related regression test yet. I will do that if there is
enough interest in this getting committed. So far no one except Andrew
and I have chimed in.

3) Requires a catalog version bump (not in posted diff)

4) The static function cleanup_path() was borrowed from

src/bin/pg_config/pg_config.c

It is a small and stable function (no change since 2010 AFAICS), so
maybe not worth the effort, but I was wondering if it should be moved
to src/common somewhere and shared.

I will add this to the next commitfest. Comments/feedback encouraged.

Joe

- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV2PykAAoJEDfy90M199hlQW0P/1fLCtFe50wFanleOxo41aki
yR8uG5vUZCLosx7lYd4+LyeE2g+bfs+fK6XgL1qIafI0zyxQSAU8TtjsIPQjjsvU
rNn1MWRrlOLEfOMMzbJPo01w5wzLhBvFzrYQ8vVtvf+T2PzjbU1hTMOcmaeXv6If
jYv0KQDgPBk/VPZ0D7MI4nYXVzNSInDLD7TGEpoTQwZ0oqvZwScSXc933isoULB4
4isael+g6mQJNoPz+OQEhUSoC922mrGs12SarfHJiUqJs1/NleClRRZ/9llCBnb2
3+zW6cb4XNh8aVP33zTtCsbrio206VjumWUYMNs546+qChormBOnYtZiIVRNRnPk
z4x/vxuhXVndDp1VnE5V5mRiW3B8ABliBf1Bcnf/Z+Gxi84LaZVtmL2hJrmn7voT
EZsQn/gmpB6ThHKbOl3t060fGZ/RAPDUwOWoYUIVcohOQqxK/iIka0bFM5cnuXO0
8oJ7CFkPSW7kBPs3uPO4Psf/jzrfaK3b/ZfitoV77sMQiVCABlR3a8khw+cPBrok
av/1afnGfz6qSxsV8sAyKUmRZkLDtmT01GUHCuujof1PQ3tD8zVsQWI3r51UcGB3
tFKvvy9koTHEunqkU6yQrCWNOEzHpGXEa1RIV33Ywgh0deKVEU5EbfJF5iIHBgOy
dYf2PHbYW7F1RSqKnZIa
=A2+X
-----END PGP SIGNATURE-----

Attachments:

pg_config_srf-20150822.1.difftext/x-diff; name=pg_config_srf-20150822.1.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ccc030f..0d2e8f1 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 425,430 ****
--- 425,433 ----
  CREATE VIEW pg_timezone_names AS
      SELECT * FROM pg_timezone_names();
  
+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+ 
  -- Statistics views
  
  CREATE VIEW pg_stat_all_tables AS
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 7889101..49c6f08 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,20 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
--- 14,33 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! # 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 = 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
diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c
index ...aa3349d .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,312 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  * ALL RIGHTS RESERVED;
+  *
+  * Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose, without fee, and without a written agreement
+  * is hereby granted, provided that the above copyright notice and this
+  * paragraph and the following two paragraphs appear in all copies.
+  *
+  * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
+  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+  * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+  * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
+  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+  *
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "catalog/pg_type.h"
+ #include "utils/elog.h"
+ #include "port.h"
+ 
+ static void cleanup_path(char *path);
+ static void get_configdata(void);
+ 
+ #ifdef PGDLLIMPORT
+ /* Postgres global */
+ extern PGDLLIMPORT char my_exec_path[];
+ #else
+ /* Postgres global */
+ extern DLLIMPORT char my_exec_path[];
+ #endif /* PGDLLIMPORT */
+ 
+ struct configdata
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ };
+ 
+ static struct configdata ConfigData[] =
+ {
+ 	{"BINDIR", NULL},
+ 	{"DOCDIR", NULL},
+ 	{"HTMLDIR", NULL},
+ 	{"INCLUDEDIR", NULL},
+ 	{"PKGINCLUDEDIR", NULL},
+ 	{"INCLUDEDIR-SERVER", NULL},
+ 	{"LIBDIR", NULL},
+ 	{"PKGLIBDIR", NULL},
+ 	{"LOCALEDIR", NULL},
+ 	{"MANDIR", NULL},
+ 	{"SHAREDIR", NULL},
+ 	{"SYSCONFDIR", NULL},
+ 	{"PGXS", NULL},
+ 	{"CONFIGURE", NULL},
+ 	{"CC", NULL},
+ 	{"CPPFLAGS", NULL},
+ 	{"CFLAGS", NULL},
+ 	{"CFLAGS_SL", NULL},
+ 	{"LDFLAGS", NULL},
+ 	{"LDFLAGS_EX", NULL},
+ 	{"LDFLAGS_SL", NULL},
+ 	{"LIBS", NULL},
+ 	{"VERSION", NULL},
+ 	{NULL, NULL}
+ };
+ 
+ static void get_configdata(void);
+ 
+ Datum pg_config(PG_FUNCTION_ARGS);
+ 
+ PG_FUNCTION_INFO_V1(pg_config);
+ 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;
+ 	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);
+ 
+ 	get_configdata();
+ 	while (ConfigData[i].name)
+ 	{
+ 		values[0] = ConfigData[i].name;
+ 		values[1] = ConfigData[i].setting;
+ 
+ 		tuple = BuildTupleFromCStrings(attinmeta, values);
+ 		tuplestore_puttuple(tupstore, tuple);
+ 		++i;
+ 	}
+ 	
+ 	/*
+ 	 * 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;
+ }
+ 
+ 
+ /*
+  * 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
+ }
+ 
+ static void
+ get_configdata(void)
+ {
+ 	char			path[MAXPGPATH];
+ 	char		   *lastsep;
+ 
+ 	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);
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ddf7c67..e375059 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5331,5336 ****
--- 5331,5340 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* pg_config */
+ DATA(insert OID = 3300 (  pg_config	PGNSP PGUID 12 1 1000 0 0 f f f f t t i 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/utils/builtins.h b/src/include/utils/builtins.h
index fc1679e..edd69f8 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1121,1126 ****
--- 1121,1129 ----
  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/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 44c6740..b4e7511 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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,
#4Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#3)
Re: exposing pg_controldata and pg_config as functions

On Sun, Aug 23, 2015 at 7:50 AM, Joe Conway <mail@joeconway.com> wrote:

1) The syntax is a bit different than what Andrew proposed:

8<----------------
select setting ~ '--with-libxml' as has_xml
from pg_config
where name = 'CONFIGURE';
has_xml
- ---------
t
(1 row)
8<----------------

In particular note that the name values are all upper case to be
consistent with pg_config, and at least currently there is no version
of the function which accepts a name as an argument (didn't seem
worthwhile to me).

Compatibility by default with the binary pg_config makes sense, users
could just wrap an SQL with lower() or upper() if needed.

2) No docs or related regression test yet. I will do that if there is
enough interest in this getting committed. So far no one except Andrew
and I have chimed in.

I think that's a good thing to have, now I have concerns about making
this data readable for non-superusers. Cloud deployments of Postgres
are logically going to block the access of this view.

4) The static function cleanup_path() was borrowed from

src/bin/pg_config/pg_config.c

cleanup_path is perhaps a candidate for src/port/path.c?

It is a small and stable function (no change since 2010 AFAICS), so
maybe not worth the effort, but I was wondering if it should be moved
to src/common somewhere and shared.

I will add this to the next commitfest. Comments/feedback encouraged.

+ Datum pg_config(PG_FUNCTION_ARGS);
+
+ PG_FUNCTION_INFO_V1(pg_config);

The declaration of the function is not needed, PG_FUNCTION_INFO_V1
takes care of it.
Regards,
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Andrew Dunstan
andrew@dunslane.net
In reply to: Michael Paquier (#4)
Re: exposing pg_controldata and pg_config as functions

On 08/23/2015 08:58 PM, Michael Paquier wrote:

2) No docs or related regression test yet. I will do that if there is
enough interest in this getting committed. So far no one except Andrew
and I have chimed in.

I think that's a good thing to have, now I have concerns about making
this data readable for non-superusers. Cloud deployments of Postgres
are logically going to block the access of this view.

I don't think it exposes any information of great security value.

+ Datum pg_config(PG_FUNCTION_ARGS);
+
+ PG_FUNCTION_INFO_V1(pg_config);

The declaration of the function is not needed, PG_FUNCTION_INFO_V1
takes care of it.

Umm, we shouldn't be using PG_FUNCTION_INFO_V1 in backend code at all, IIRC.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Andrew Dunstan (#5)
Re: exposing pg_controldata and pg_config as functions

Andrew Dunstan <andrew@dunslane.net> writes:

On 08/23/2015 08:58 PM, Michael Paquier wrote:

I think that's a good thing to have, now I have concerns about making
this data readable for non-superusers. Cloud deployments of Postgres
are logically going to block the access of this view.

I don't think it exposes any information of great security value.

We just had that kerfuffle about whether WAL compression posed a security
risk; doesn't that imply that at least the data relevant to WAL position
has to be unreadable by non-superusers?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Joe Conway
mail@joeconway.com
In reply to: Tom Lane (#6)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/24/2015 06:50 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 08/23/2015 08:58 PM, Michael Paquier wrote:

I think that's a good thing to have, now I have concerns about
making this data readable for non-superusers. Cloud deployments
of Postgres are logically going to block the access of this
view.

I don't think it exposes any information of great security
value.

We just had that kerfuffle about whether WAL compression posed a
security risk; doesn't that imply that at least the data relevant
to WAL position has to be unreadable by non-superusers?

So pg_config might be fully unrestricted, but pg_controldata might
need certain rows filtered based on superuser status? Do you think
those rows should be present but redacted, or completely filtered out?

Joe
- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV2z3SAAoJEDfy90M199hlgmwP/iPI4gJAM00b1mWiPHYSEMjQ
pdVgPkFgfGQKyTizo7rEv1nJTQI3J9aUD7hvqYvPGlSOum0xei17fiRUIKnfqGgZ
7aSuhc97gZ7U5LvDsClovEUDEon+RIibZAYHKnKv2qYDwO/ZvfdFFQNi9TV0eREi
QrEYafNo3/PWqJtrJoqhXaXyXsZ33FKtaaesQZJXvUUkTaE42eviq0cPiz2lHEsq
szlGBnPkBS3qthAusApetAobZH9OymL4yl1BWwmBl3d2nEvQ4OVFGWo195It4XyQ
98bMzXse0PvBuKkcKrlTjxPdtR9UE/2FHojh7VLaj+JQeCGjehXNuogGPr7XHNSu
cbCvIWsxW7Vz1liwFxY9I7Aui6/4X/oPehrct4CqaihqoztP1JrkQpVJDBYWwAhH
Q/sRe8gUY8AWQHQljt9nuZvXmEYBnFbSf8tWVZ3/yhU1fK9dcl9B5doIHwKQXXtW
+BHx4mOX5gcSRvGQFkJO0auE3Y9dvfUtpV4xDC57OHekgKA+rZw/HtElwKIhgrHI
QoCd9PpJdG3UngX7ffsRuhJIhTUCSOKA2AIdceRyH4UgtqtHLzSU1tom3XMcQD+f
mJvlKMwSvqh2Qmd/ZiNhgN4APkGk1AmH26hMMhI9HIrAIghkmPDfssLxYcBgJyDd
lt8dJLQDnaddFLuvdQww
=KZVU
-----END PGP SIGNATURE-----

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Joe Conway
mail@joeconway.com
In reply to: Andrew Dunstan (#5)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/24/2015 04:38 AM, Andrew Dunstan wrote:

On 08/23/2015 08:58 PM, Michael Paquier wrote:

+ Datum pg_config(PG_FUNCTION_ARGS); + +
PG_FUNCTION_INFO_V1(pg_config);

The declaration of the function is not needed,
PG_FUNCTION_INFO_V1 takes care of it.

Umm, we shouldn't be using PG_FUNCTION_INFO_V1 in backend code at
all, IIRC.

Right -- those lines are a cut-n-pasteo from the original pg_config as
an extension code. Will fix.

Thanks,

Joe

- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV20DqAAoJEDfy90M199hlA9wP/RtnahsLzmbsXfPssTGUfdHu
nuiF5Sgpqn5/tMNakNVr/ACBiSZQeFUf3FQNRzyoOK6zMXCox4HbSFKi4u0UxpYV
CZZgIKByf4xaHjaZGpnY5dTteBQXdRv3Dp85hhmVbDHbO80+7e7zf0BI5QrUm14E
Nhv6PJn6NMBm/GJrvm76+lDt7DJWYygi/3Jupn/aQNpPCZ5bHP+e4e/NC2FMtW3y
Knm+KN5YEA0IWZKnM0s9kIfYeI9PE2tsF3jpdw8U7BTzziLf/6yTVXlJ/5xj0CfU
1kubgcZTp5UhDOWD7RuRQ6WUzbye/Yd/+9C9SNYltidZY7tnlbRyb0J6QerXNakM
tM+eXAbroXnfAZulq8YJO8nFPviXh6Y4F1pEXtpVJIuLTu9NXEDhRLPAzsSXciCa
yQuq98L2UmzpSr9i5ETMmjb7mUPcS9/IR/FQldNgP1/ARY2CTyL6hbdCJH8QieVo
plEUaYPz4QbKTyF/OZsuamDSpqun412Zs/LRgF5kQhIcI1Q0z9SJ4GwQUZb/bwQm
c0ztQnW8AKtzBgGVCYoJSKd4bD5w/Qtv8WdoZJzXnu3GOvq/laS+kCaBcYl3N7Rf
dwDKpYmWitmyIT0THzhdCiJ38rMgq/JjmhCJQiMJJxvvsadl/mPKUO6k4ZUMNw6O
BKrHf/JETN/Wnqd1IPqq
=ohKC
-----END PGP SIGNATURE-----

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Joe Conway
mail@joeconway.com
In reply to: Joe Conway (#7)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/24/2015 08:52 AM, Joe Conway wrote:

On 08/24/2015 06:50 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 08/23/2015 08:58 PM, Michael Paquier wrote:

I think that's a good thing to have, now I have concerns
about making this data readable for non-superusers. Cloud
deployments of Postgres are logically going to block the
access of this view.

I don't think it exposes any information of great security
value.

We just had that kerfuffle about whether WAL compression posed a
security risk; doesn't that imply that at least the data
relevant to WAL position has to be unreadable by non-superusers?

So pg_config might be fully unrestricted, but pg_controldata might
need certain rows filtered based on superuser status? Do you think
those rows should be present but redacted, or completely filtered
out?

Here is the next installment on this. It addresses most of the
previous comments, but does not yet address the issue raised here by Tom
.

Changes:
1.) pg_controldata() function and pg_controldata view added

2.) cleanup_path() moved to port/path.c

3.) extra PG_FUNCTION_INFO_V1() noise removed

Issues needing comment:
a.) Which items need hiding from non-superusers and should the value be
redacted or the entire result set row be suppressed?

b.) There is a difference in the formatting of timestamps between the
pg_controldata binary and the builtin function. To see it do:

  diff -c <(pg_controldata) \
  <(psql -qAt -c "select rpad(name || ':', 38) || setting
                  from pg_controldata")

What I see is:

pg_controldata
! pg_control last modified: Tue 25 Aug 2015 08:10:42 PM PDT
pg_controldata()
! pg_control last modified: Tue Aug 25 20:10:42 2015

Does it matter?

c.) There is some common code between pg_controldata.c in bin and
pg_controldata.c in backend/utils/misc. Specifically the string
definitions in printf statements match those in ControlData[], and
dbState() and wal_level_str() are in both places. Any opinions
on consolidating them in src/common somewhere?

d.) Still no docs yet - will get to it eventually.

Thanks,

Joe

- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV3TNnAAoJEDfy90M199hl0OsQAIyTr0hqxwjrGenDpnS4qE8u
UJWVeCpqehFIobxcJ0TICTaQX835fzPGJIiTUwI1Dmz9sgjipvSG1wBmD4bRT93X
WO4e/+Yr5onZ9vNVXlUswPK2kuzehImhmzMSnJ6KDXKkfw2MOZmz56wBb3yIB3lq
K44FDukZ01w9RCGM8H5B/MPNMHIqfBB4wdlKARJZhqeUyPvTJ71iMiqeE77v3AIH
JLmW6kRw8c3NVu/Wa+GVz4FGjIZKR5oazlFYfDTeHXrxV8NIDUFNrKikAW1ScdVK
qSPVjFxoUlbX4W2dd1L1ciGeq83DktYbdKtpZZScQGXwhuq7Y1fHZQwzlxlraB/c
UiqNdxmi7IeUdOIncsKPDmjs7C5yeNj1CRnWHTAQRW98RM42A3TvT2Qlkxm0CVLQ
lZjFVOOMIf4pXYQv6PfiicO6QWYTUSXCa891s/10H2xkS/sMK1yHz3DWSZxVdDdI
dbh5gie/GFro1nwWd8gjkn5KCe917GDBAUBn+QE5TgUPnRhserq6FQBSyVXfZtOQ
o6nRM8vuv9Y06CRoeIgagtDWxippl0OAw442wHyme/PBQZ2842PW8GNNqw+B1HWz
Ir0V5FiZdLLQipwiKT152+8OsOa/NU6wxGFuJr8It/4471h3jU5dxuHO+wQqMDEb
xCn6ebwZaa9oSjHFrfy3
=oMOO
-----END PGP SIGNATURE-----

Attachments:

2015082503-pgconfig_controldata.difftext/x-diff; name=2015082503-pgconfig_controldata.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ccc030f..7b5db32 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 425,430 ****
--- 425,436 ----
  CREATE VIEW pg_timezone_names AS
      SELECT * FROM pg_timezone_names();
  
+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+ 
+ CREATE VIEW pg_controldata AS
+     SELECT * FROM pg_controldata();
+ 
  -- Statistics views
  
  CREATE VIEW pg_stat_all_tables AS
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 7889101..13b60f7 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,34 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! # 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 = guc.o help_config.o pg_config.o pg_controldata.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
index ...475a9c5 .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,280 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  * ALL RIGHTS RESERVED;
+  *
+  * Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose, without fee, and without a written agreement
+  * is hereby granted, provided that the above copyright notice and this
+  * paragraph and the following two paragraphs appear in all copies.
+  *
+  * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
+  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+  * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+  * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
+  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+  *
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "catalog/pg_type.h"
+ #include "utils/builtins.h"
+ #include "utils/elog.h"
+ #include "port.h"
+ 
+ static void get_configdata(void);
+ 
+ #ifdef PGDLLIMPORT
+ /* Postgres global */
+ extern PGDLLIMPORT char my_exec_path[];
+ #else
+ /* Postgres global */
+ extern DLLIMPORT char my_exec_path[];
+ #endif /* PGDLLIMPORT */
+ 
+ struct configdata
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ };
+ 
+ static struct configdata ConfigData[] =
+ {
+ 	{"BINDIR", NULL},
+ 	{"DOCDIR", NULL},
+ 	{"HTMLDIR", NULL},
+ 	{"INCLUDEDIR", NULL},
+ 	{"PKGINCLUDEDIR", NULL},
+ 	{"INCLUDEDIR-SERVER", NULL},
+ 	{"LIBDIR", NULL},
+ 	{"PKGLIBDIR", NULL},
+ 	{"LOCALEDIR", NULL},
+ 	{"MANDIR", NULL},
+ 	{"SHAREDIR", NULL},
+ 	{"SYSCONFDIR", NULL},
+ 	{"PGXS", NULL},
+ 	{"CONFIGURE", NULL},
+ 	{"CC", NULL},
+ 	{"CPPFLAGS", NULL},
+ 	{"CFLAGS", NULL},
+ 	{"CFLAGS_SL", NULL},
+ 	{"LDFLAGS", NULL},
+ 	{"LDFLAGS_EX", NULL},
+ 	{"LDFLAGS_SL", NULL},
+ 	{"LIBS", NULL},
+ 	{"VERSION", NULL},
+ 	{NULL, NULL}
+ };
+ 
+ static void get_configdata(void);
+ 
+ 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;
+ 	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);
+ 
+ 	get_configdata();
+ 	while (ConfigData[i].name)
+ 	{
+ 		values[0] = ConfigData[i].name;
+ 		values[1] = ConfigData[i].setting;
+ 
+ 		tuple = BuildTupleFromCStrings(attinmeta, values);
+ 		tuplestore_puttuple(tupstore, tuple);
+ 		++i;
+ 	}
+ 	
+ 	/*
+ 	 * 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;
+ }
+ 
+ 
+ static void
+ get_configdata(void)
+ {
+ 	char			path[MAXPGPATH];
+ 	char		   *lastsep;
+ 
+ 	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);
+ }
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index ...e8aa367 .
*** a/src/backend/utils/misc/pg_controldata.c
--- b/src/backend/utils/misc/pg_controldata.c
***************
*** 0 ****
--- 1,515 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_controldata.c
+  *		Expose same output as pg_controldata except as an SRF
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  * ALL RIGHTS RESERVED;
+  *
+  * Permission to use, copy, modify, and distribute this software and its
+  * documentation for any purpose, without fee, and without a written agreement
+  * is hereby granted, provided that the above copyright notice and this
+  * paragraph and the following two paragraphs appear in all copies.
+  *
+  * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
+  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+  * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+  * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGE.
+  *
+  * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+  * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO
+  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+  *
+  */
+ 
+ #include "postgres.h"
+ 
+ #include <unistd.h>
+ #include <time.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "port/pg_crc32c.h"
+ #include "utils/builtins.h"
+ 
+ struct controldata
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ };
+ 
+ static struct controldata ControlData[] =
+ {
+ 	{gettext_noop("pg_control version number"), NULL},
+ 	{gettext_noop("Catalog version number"), NULL},
+ 	{gettext_noop("Database system identifier"), NULL},
+ 	{gettext_noop("Database cluster state"), NULL},
+ 	{gettext_noop("pg_control last modified"), NULL},
+ 	{gettext_noop("Latest checkpoint location"), NULL},
+ 	{gettext_noop("Prior checkpoint location"), NULL},
+ 	{gettext_noop("Latest checkpoint's REDO location"), NULL},
+ 	{gettext_noop("Latest checkpoint's REDO WAL file"), NULL},
+ 	{gettext_noop("Latest checkpoint's TimeLineID"), NULL},
+ 	{gettext_noop("Latest checkpoint's PrevTimeLineID"), NULL},
+ 	{gettext_noop("Latest checkpoint's full_page_writes"), NULL},
+ 	{gettext_noop("Latest checkpoint's NextXID"), NULL},
+ 	{gettext_noop("Latest checkpoint's NextOID"), NULL},
+ 	{gettext_noop("Latest checkpoint's NextMultiXactId"), NULL},
+ 	{gettext_noop("Latest checkpoint's NextMultiOffset"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestXID"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestXID's DB"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestActiveXID"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestMultiXid"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestMulti's DB"), NULL},
+ 	{gettext_noop("Latest checkpoint's oldestCommitTs"), NULL},
+ 	{gettext_noop("Time of latest checkpoint"), NULL},
+ 	{gettext_noop("Fake LSN counter for unlogged rels"), NULL},
+ 	{gettext_noop("Minimum recovery ending location"), NULL},
+ 	{gettext_noop("Min recovery ending loc's timeline"), NULL},
+ 	{gettext_noop("Backup start location"), NULL},
+ 	{gettext_noop("Backup end location"), NULL},
+ 	{gettext_noop("End-of-backup record required"), NULL},
+ 	{gettext_noop("wal_level setting"), NULL},
+ 	{gettext_noop("wal_log_hints setting"), NULL},
+ 	{gettext_noop("max_connections setting"), NULL},
+ 	{gettext_noop("max_worker_processes setting"), NULL},
+ 	{gettext_noop("max_prepared_xacts setting"), NULL},
+ 	{gettext_noop("max_locks_per_xact setting"), NULL},
+ 	{gettext_noop("track_commit_timestamp setting"), NULL},
+ 	{gettext_noop("Maximum data alignment"), NULL},
+ 	{gettext_noop("Database block size"), NULL},
+ 	{gettext_noop("Blocks per segment of large relation"), NULL},
+ 	{gettext_noop("WAL block size"), NULL},
+ 	{gettext_noop("Bytes per WAL segment"), NULL},
+ 	{gettext_noop("Maximum length of identifiers"), NULL},
+ 	{gettext_noop("Maximum columns in an index"), NULL},
+ 	{gettext_noop("Maximum size of a TOAST chunk"), NULL},
+ 	{gettext_noop("Size of a large-object chunk"), NULL},
+ 	{gettext_noop("Date/time type storage"), NULL},
+ 	{gettext_noop("Float4 argument passing"), NULL},
+ 	{gettext_noop("Float8 argument passing"), NULL},
+ 	{gettext_noop("Data page checksum version"), NULL},
+ 	{NULL, NULL}
+ };
+ 
+ 
+ static const char *dbState(DBState state);
+ static void get_controldata(void);
+ 
+ Datum
+ pg_controldata(PG_FUNCTION_ARGS)
+ {
+ 	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	Tuplestorestate	   *tupstore;
+ 	HeapTuple			tuple;
+ 	TupleDesc			tupdesc;
+ 	AttInMetadata	   *attinmeta;
+ 	MemoryContext		per_query_ctx;
+ 	MemoryContext		oldcontext;
+ 	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);
+ 
+ 	get_controldata();
+ 	while (ControlData[i].name)
+ 	{
+ 		values[0] = _(ControlData[i].name);
+ 		values[1] = ControlData[i].setting;
+ 
+ 		tuple = BuildTupleFromCStrings(attinmeta, values);
+ 		tuplestore_puttuple(tupstore, tuple);
+ 		++i;
+ 	}
+ 	
+ 	/*
+ 	 * 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;
+ }
+ 
+ static const char *
+ dbState(DBState state)
+ {
+ 	switch (state)
+ 	{
+ 		case DB_STARTUP:
+ 			return _("starting up");
+ 		case DB_SHUTDOWNED:
+ 			return _("shut down");
+ 		case DB_SHUTDOWNED_IN_RECOVERY:
+ 			return _("shut down in recovery");
+ 		case DB_SHUTDOWNING:
+ 			return _("shutting down");
+ 		case DB_IN_CRASH_RECOVERY:
+ 			return _("in crash recovery");
+ 		case DB_IN_ARCHIVE_RECOVERY:
+ 			return _("in archive recovery");
+ 		case DB_IN_PRODUCTION:
+ 			return _("in production");
+ 	}
+ 	return _("unrecognized status code");
+ }
+ 
+ static const char *
+ wal_level_str(WalLevel wal_level)
+ {
+ 	switch (wal_level)
+ 	{
+ 		case WAL_LEVEL_MINIMAL:
+ 			return "minimal";
+ 		case WAL_LEVEL_ARCHIVE:
+ 			return "archive";
+ 		case WAL_LEVEL_HOT_STANDBY:
+ 			return "hot_standby";
+ 		case WAL_LEVEL_LOGICAL:
+ 			return "logical";
+ 	}
+ 	return _("unrecognized wal_level");
+ }
+ 
+ static void
+ get_controldata(void)
+ {
+ 	ControlFileData ControlFile;
+ 	int				fd;
+ 	char			ControlFilePath[MAXPGPATH];
+ 	pg_crc32c		crc;
+ 	time_t			time_tmp;
+ 	char			pgctime_str[128];
+ 	char			ckpttime_str[128];
+ 	char			sysident_str[32];
+ 	const char	   *strftime_fmt = "%c";
+ 	XLogSegNo		segno;
+ 	char			xlogfilename[MAXFNAMELEN];
+ 	StringInfoData	buf;
+ 
+ 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+ 
+ 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+ 		elog(ERROR, "could not open file \"%s\" for reading: %s",
+ 					 ControlFilePath, strerror(errno));
+ 
+ 	if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+ 		elog(ERROR, "could not read file \"%s\": %s",
+ 					 ControlFilePath, strerror(errno));
+ 
+ 	close(fd);
+ 
+ 	/* Check the CRC. */
+ 	INIT_CRC32C(crc);
+ 	COMP_CRC32C(crc,
+ 			   (char *) &ControlFile,
+ 			   offsetof(ControlFileData, crc));
+ 	FIN_CRC32C(crc);
+ 
+ 	if (!EQ_CRC32C(crc, ControlFile.crc))
+ 		elog(ERROR, "calculated CRC checksum does not match value stored in file");
+ 
+ 	/*
+ 	 * This slightly-chintzy coding will work as long as the control file
+ 	 * timestamps are within the range of time_t; that should be the case in
+ 	 * all foreseeable circumstances, so we don't bother importing the
+ 	 * backend's timezone library.
+ 	 *
+ 	 * Use variable for format to suppress overly-anal-retentive gcc warning
+ 	 * about %c
+ 	 */
+ 	time_tmp = (time_t) ControlFile.time;
+ 	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 	time_tmp = (time_t) ControlFile.checkPointCopy.time;
+ 	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile.checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/*
+ 	 * Format system_identifier separately to keep platform-dependent format
+ 	 * code out of the translatable message string.
+ 	 */
+ 	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
+ 			 ControlFile.system_identifier);
+ 
+ 	initStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.pg_control_version);
+ 	ControlData[0].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.catalog_version_no);
+ 	ControlData[1].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", sysident_str);
+ 	ControlData[2].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", dbState(ControlFile.state));
+ 	ControlData[3].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", pgctime_str);
+ 	ControlData[4].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.checkPoint >> 32),
+ 					 (uint32) ControlFile.checkPoint);
+ 	ControlData[5].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.prevCheckPoint >> 32),
+ 					 (uint32) ControlFile.prevCheckPoint);
+ 	ControlData[6].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.checkPointCopy.redo >> 32),
+ 					 (uint32) ControlFile.checkPointCopy.redo);
+ 	ControlData[7].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", xlogfilename);
+ 	ControlData[8].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.ThisTimeLineID);
+ 	ControlData[9].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.PrevTimeLineID);
+ 	ControlData[10].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
+ 	ControlData[11].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u/%u",
+ 					 ControlFile.checkPointCopy.nextXidEpoch,
+ 					 ControlFile.checkPointCopy.nextXid);
+ 	ControlData[12].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextOid);
+ 	ControlData[13].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextMulti);
+ 	ControlData[14].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.nextMultiOffset);
+ 	ControlData[15].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestXid);
+ 	ControlData[16].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestXidDB);
+ 	ControlData[17].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestActiveXid);
+ 	ControlData[18].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestMulti);
+ 	ControlData[19].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestMultiDB);
+ 	ControlData[20].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.checkPointCopy.oldestCommitTs);
+ 	ControlData[21].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", ckpttime_str);
+ 	ControlData[22].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.unloggedLSN >> 32),
+ 					 (uint32) ControlFile.unloggedLSN);
+ 	ControlData[23].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.minRecoveryPoint >> 32),
+ 					 (uint32) ControlFile.minRecoveryPoint);
+ 	ControlData[24].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.minRecoveryPointTLI);
+ 	ControlData[25].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.backupStartPoint >> 32),
+ 					 (uint32) ControlFile.backupStartPoint);
+ 	ControlData[26].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%X/%X",
+ 					 (uint32) (ControlFile.backupEndPoint >> 32),
+ 					 (uint32) ControlFile.backupEndPoint);
+ 	ControlData[27].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 ControlFile.backupEndRequired ? _("yes") : _("no"));
+ 	ControlData[28].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s", wal_level_str(ControlFile.wal_level));
+ 	ControlData[29].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 ControlFile.wal_log_hints ? _("on") : _("off"));
+ 	ControlData[30].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%d", ControlFile.MaxConnections);
+ 	ControlData[31].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%d", ControlFile.max_worker_processes);
+ 	ControlData[32].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%d", ControlFile.max_prepared_xacts);
+ 	ControlData[33].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%d", ControlFile.max_locks_per_xact);
+ 	ControlData[34].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 ControlFile.track_commit_timestamp ? _("on") : _("off"));
+ 	ControlData[35].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.maxAlign);
+ 	ControlData[36].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.blcksz);
+ 	ControlData[37].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.relseg_size);
+ 	ControlData[38].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.xlog_blcksz);
+ 	ControlData[39].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.xlog_seg_size);
+ 	ControlData[40].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.nameDataLen);
+ 	ControlData[41].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.indexMaxKeys);
+ 	ControlData[42].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.toast_max_chunk_size);
+ 	ControlData[43].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.loblksize);
+ 	ControlData[44].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
+ 	ControlData[45].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 (ControlFile.float4ByVal ? _("by value") : _("by reference")));
+ 	ControlData[46].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%s",
+ 					 (ControlFile.float8ByVal ? _("by value") : _("by reference")));
+ 	ControlData[47].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ 
+ 	appendStringInfo(&buf, "%u", ControlFile.data_checksum_version);
+ 	ControlData[48].setting = pstrdup(buf.data);
+ 	resetStringInfo(&buf);
+ }
diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c
index 9b1f95d..25efc71 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 29,65 ****
  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
--- 29,34 ----
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ddf7c67..e0ba271 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5331,5336 ****
--- 5331,5344 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* pg_config */
+ DATA(insert OID = 3300 (  pg_config	PGNSP PGUID 12 1 1000 0 0 f f f f t t i 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_config _null_ _null_ _null_ ));
+ DESCR("pg_config binary as a function");
+ 
+ /* pg_controldata */
+ DATA(insert OID = 3308 (  pg_controldata	PGNSP PGUID 12 1 1000 0 0 f f f f t t i 0 0 2249 "" "{25,25}" "{o,o}" "{name,setting}" _null_ _null_ pg_controldata _null_ _null_ _null_ ));
+ DESCR("pg_controldata 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/port.h b/src/include/port.h
index 3787cbf..ab38f62 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 fc1679e..4a67a12 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1121,1126 ****
--- 1121,1132 ----
  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);
+ 
+ /* pg_controldata.c */
+ extern Datum pg_controldata(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 d0f72df..7613666 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 44c6740..85b4082 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1316 ----
      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_controldata| SELECT pg_controldata.name,
+     pg_controldata.setting
+    FROM pg_controldata() pg_controldata(name, setting);
  pg_cursors| SELECT c.name,
      c.statement,
      c.is_holdable,
#10Stephen Frost
sfrost@snowman.net
In reply to: Joe Conway (#9)
Re: exposing pg_controldata and pg_config as functions

* Joe Conway (mail@joeconway.com) wrote:

Issues needing comment:
a.) Which items need hiding from non-superusers and should the value be
redacted or the entire result set row be suppressed?

I'm of the opinion that we need to at least redact it and that what we
should do is simply suppress the entire result set until we provide a
way for administrators to manage who can access it (eg: default roles,
this one would fall under 'pg_monitor', imo).

b.) There is a difference in the formatting of timestamps between the
pg_controldata binary and the builtin function. To see it do:

diff -c <(pg_controldata) \
<(psql -qAt -c "select rpad(name || ':', 38) || setting
from pg_controldata")

What I see is:

pg_controldata
! pg_control last modified: Tue 25 Aug 2015 08:10:42 PM PDT
pg_controldata()
! pg_control last modified: Tue Aug 25 20:10:42 2015

Does it matter?

I don't think we can help that, can we? At the least, the
pg_controldata() output should match what the GUCs and whatnot tell us
to do, but the pg_controldata file needs to include things like the
timezone. In the end, I don't believe we need to make them match and
trying to would just make things ugly.

c.) There is some common code between pg_controldata.c in bin and
pg_controldata.c in backend/utils/misc. Specifically the string
definitions in printf statements match those in ControlData[], and
dbState() and wal_level_str() are in both places. Any opinions
on consolidating them in src/common somewhere?

Haven't got any great ideas about exactly how to consolidate them, but I
do think it'd be good to do so..

Thanks!

Stephen

#11Joe Conway
mail@joeconway.com
In reply to: Stephen Frost (#10)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 08/26/2015 06:33 AM, Stephen Frost wrote:

* Joe Conway (mail@joeconway.com) wrote:

Issues needing comment: a.) Which items need hiding from
non-superusers and should the value be redacted or the entire
result set row be suppressed?

I'm of the opinion that we need to at least redact it and that what
we should do is simply suppress the entire result set until we
provide a way for administrators to manage who can access it (eg:
default roles, this one would fall under 'pg_monitor', imo).

Whatever it is it would have to be available during initdb. And in any
case I'm no closer to knowing which rows to hide/redact/suppress other
than WAL position. Possibly the thing to do for now would be to revoke
public from these?

Joe
- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV3c3nAAoJEDfy90M199hlMnYQAJliTc7bJTCndMQ0emN6xV55
DqODtxABxh3kqPmWcvSO08dSZ5yHpCKYgIm8OmRIpfDUwNID1uBXsO5pRd1XVzLr
42OmQ9QauAZ9+f/Rea668U/zgzhnIJXdVsFfAmum516UoR3W1rqW5ggPKgN5YDhC
9Z6ikL1fs6l6l1yrvaefbepS1FLx6wDplhctN+hbEdHw9gwAf67fv7ncaPZ4BRyc
hogL4mXoz0fFQz7RDvnR2g0uu17k3imbwzqGiyJwH4+9cfnNLWrBXupKwC06ufWF
t3cLh4lLTUhx/2amB0qKMQp1MgVs6r70f5ciFTWvaO0nro0wSGHnIsnqFDOfnv2X
kctZreHs7gDAFXWM4Qp45oxTHy6Lfce75IvDfZGZ3y8NOhEHZDqJs6VIdOgCu4h0
RkJE/RrRz7ZtMAhyokxWMZvffYRutLPbXAUvg6TBeDVy1T7SKoQK81IBz/Nkd+Bm
WkB/EFklUZw/B2HnDpXRV3tdjAzMAJw22bQi0Y7515K25w7NC2nquzX1eBMGmaqe
yDf/gobFg601E9WMjaNoxMGy3Niigk46UsQLGT7RJ/ciojY1gGQh/qd4b1BeJpM0
kRmj0Jsyn0cO8hs6h7jBNBVJjlBhr9ijd4tWaZAk9XqLExPPmGunhcoOMf6ttmvy
533U1P2OKyGBZZissMd4
=dlGD
-----END PGP SIGNATURE-----

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Stephen Frost
sfrost@snowman.net
In reply to: Joe Conway (#11)
Re: exposing pg_controldata and pg_config as functions

* Joe Conway (mail@joeconway.com) wrote:

On 08/26/2015 06:33 AM, Stephen Frost wrote:

* Joe Conway (mail@joeconway.com) wrote:

Issues needing comment: a.) Which items need hiding from
non-superusers and should the value be redacted or the entire
result set row be suppressed?

I'm of the opinion that we need to at least redact it and that what
we should do is simply suppress the entire result set until we
provide a way for administrators to manage who can access it (eg:
default roles, this one would fall under 'pg_monitor', imo).

Whatever it is it would have to be available during initdb. And in any
case I'm no closer to knowing which rows to hide/redact/suppress other
than WAL position. Possibly the thing to do for now would be to revoke
public from these?

That was my thinking- revoke public from them. The default roles, based
on the last patch anyway, are available at initdb time and when
system_views.sql is run.

Thanks!

Stehpen

#13Peter Eisentraut
peter_e@gmx.net
In reply to: Andrew Dunstan (#1)
Re: exposing pg_controldata and pg_config as functions

On 8/20/15 9:59 AM, Andrew Dunstan wrote:

The regression tests thus passed, but should not have. It occurred to me
that if we had a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

This particular case could probably be addressed in a less roundabout
way by enhancing the mapping mechanism in pg_regress.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Peter Eisentraut
peter_e@gmx.net
In reply to: Tom Lane (#6)
Re: exposing pg_controldata and pg_config as functions

On 8/24/15 9:50 AM, Tom Lane wrote:

Andrew Dunstan <andrew@dunslane.net> writes:

On 08/23/2015 08:58 PM, Michael Paquier wrote:

I think that's a good thing to have, now I have concerns about making
this data readable for non-superusers. Cloud deployments of Postgres
are logically going to block the access of this view.

I don't think it exposes any information of great security value.

We just had that kerfuffle about whether WAL compression posed a security
risk; doesn't that imply that at least the data relevant to WAL position
has to be unreadable by non-superusers?

We already have functions that expose the current (or recent, or
interesting) WAL position, so any new ones should probably follow the
existing ones. Or possibly we don't need any new ones, because we
already have enough?

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Peter Eisentraut
peter_e@gmx.net
In reply to: Joe Conway (#9)
Re: exposing pg_controldata and pg_config as functions

On 8/25/15 11:32 PM, Joe Conway wrote:

1.) pg_controldata() function and pg_controldata view added

I don't think dumping out whatever pg_controldata happens to print as a
bunch of text fields is very sophisticated. We have functionality to
compute with WAL positions, for example, and they won't be of much use
if this is going to be all text.

Also, the GUC settings tracked in pg_control can already be viewed using
normal mechanisms, so we don't need a second way to see them.

The fact that some of this is stored in pg_control and some is not is
really an implementation detail. We should be thinking of ways to
expose specific useful information in useful ways, not just dump out
everything we can find. Ultimately, I think we would like to move away
from people parsing textual pg_controldata output, but this proposal is
not moving very far in that direction.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Robert Haas
robertmhaas@gmail.com
In reply to: Peter Eisentraut (#15)
Re: exposing pg_controldata and pg_config as functions

On Mon, Aug 31, 2015 at 9:47 PM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 8/25/15 11:32 PM, Joe Conway wrote:

1.) pg_controldata() function and pg_controldata view added

I don't think dumping out whatever pg_controldata happens to print as a
bunch of text fields is very sophisticated. We have functionality to
compute with WAL positions, for example, and they won't be of much use
if this is going to be all text.

Also, the GUC settings tracked in pg_control can already be viewed using
normal mechanisms, so we don't need a second way to see them.

The fact that some of this is stored in pg_control and some is not is
really an implementation detail. We should be thinking of ways to
expose specific useful information in useful ways, not just dump out
everything we can find. Ultimately, I think we would like to move away
from people parsing textual pg_controldata output, but this proposal is
not moving very far in that direction.

The nice thing about dumping the information as text is that you can
return every value in the same universal format: text. There's a lot
to like about that.

But I'm not sure I like the idea of adding a server dependency on the
ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#16)
Re: exposing pg_controldata and pg_config as functions

Robert Haas <robertmhaas@gmail.com> writes:

But I'm not sure I like the idea of adding a server dependency on the
ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

I hadn't been paying attention --- the proposed patch actually depends on
exec'ing pg_controldata? That's horrid! There is no expectation that
that's installed.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Joe Conway
mail@joeconway.com
In reply to: Tom Lane (#17)
Re: exposing pg_controldata and pg_config as functions

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09/02/2015 05:25 PM, Tom Lane wrote:

Robert Haas <robertmhaas@gmail.com> writes:

But I'm not sure I like the idea of adding a server dependency on
the ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

I hadn't been paying attention --- the proposed patch actually
depends on exec'ing pg_controldata? That's horrid! There is no
expectation that that's installed.

No it doesn't. I'm confused :-/

Joe

- --
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJV52qoAAoJEDfy90M199hltvwP/3MfUQfPPglhBuY1V3CTzHu9
kTw5tNuGI/244Yc11wLtV07W+3QWXzCNY/fL1JCW5ns51mTfZKfkskNWD0O9fIex
gvK4p/3Z+y344qsdDlzbzw0A/PU05UCq1UlgXCF6nyQJW6cZfaCckbEpZWVK/uV7
aYokIqnIiilWaPu224b6jBOukK7oizEjXevdFBafLbetLJHMx+9k8LMbpPdieAm/
RSk17+N77WQ2zTFHcdz8U1MYAbaokmv155s1vUFgrqOUJGc0r6K+vImKgxOjbbmg
pv2jf7vvUwwjUy7f2iPhWJAKfGCV1m9ovWaXsMYqcF55JwSzvP8B2htUtM4Lr1qF
SsWO7e36bLoH++yAGfKp7oZIhA9r6SR6cwEoCvso3immZ2zhOzbRcw4tI4pE9fhB
P/mEbKFF5BsGHjeslB8RrMQG68DxEwPkaafH4mc1QjKiXNfWPH9ci+pgfSLVphJq
gn+ZuPrReIFhQKyMchcvZVVWJd9Dt02D2lsIzUfBWGXwOTLFVikD6BC6siy5KWmy
xuEGLEfts9E7gPD3qPXxNuY7TCvb+L7R+1C9/M5diiV7rerMUocH/RqrPP6nXHTc
BdfJhzOfU+H+Kt0nbdE8Vjw3BOKT6nqT0kc+le+F/Q1h2XLB63KhaOkFzVW73Rfd
JRRqkyks+eVgEn2I4OKm
=OAms
-----END PGP SIGNATURE-----

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Tom Lane (#17)
Re: exposing pg_controldata and pg_config as functions

Tom Lane wrote:

Robert Haas <robertmhaas@gmail.com> writes:

But I'm not sure I like the idea of adding a server dependency on the
ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

I hadn't been paying attention --- the proposed patch actually depends on
exec'ing pg_controldata? That's horrid! There is no expectation that
that's installed.

No, it doesn't. For the pg_controldata output it processes the
pg_control file directly, and for pg_config it relies on compile-time
CPPFLAGS.

I think trying to duplicate the exact strings isn't too nice an
interface.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Josh Berkus
josh@agliodbs.com
In reply to: Joe Conway (#2)
Re: exposing pg_controldata and pg_config as functions

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

Tom Lane wrote:

Robert Haas <robertmhaas@gmail.com> writes:

But I'm not sure I like the idea of adding a server dependency on the
ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

I hadn't been paying attention --- the proposed patch actually depends on
exec'ing pg_controldata? That's horrid! There is no expectation that
that's installed.

No, it doesn't. For the pg_controldata output it processes the
pg_control file directly, and for pg_config it relies on compile-time
CPPFLAGS.

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Josh Berkus (#20)
Re: exposing pg_controldata and pg_config as functions

Josh Berkus wrote:

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

I was primarily looking at pg_controldata, so we agree there.

As for pg_config, I'm confused about its usefulness -- which of these
lines are useful in the SQL interface? Anyway, I don't see anything
better than a couple of text columns for this case.

BINDIR = /home/alvherre/Code/pgsql/install/master/bin
DOCDIR = /home/alvherre/Code/pgsql/install/master/share/doc
HTMLDIR = /home/alvherre/Code/pgsql/install/master/share/doc
INCLUDEDIR = /home/alvherre/Code/pgsql/install/master/include
PKGINCLUDEDIR = /home/alvherre/Code/pgsql/install/master/include
INCLUDEDIR-SERVER = /home/alvherre/Code/pgsql/install/master/include/server
LIBDIR = /home/alvherre/Code/pgsql/install/master/lib
PKGLIBDIR = /home/alvherre/Code/pgsql/install/master/lib
LOCALEDIR = /home/alvherre/Code/pgsql/install/master/share/locale
MANDIR = /home/alvherre/Code/pgsql/install/master/share/man
SHAREDIR = /home/alvherre/Code/pgsql/install/master/share
SYSCONFDIR = /home/alvherre/Code/pgsql/install/master/etc
PGXS = /home/alvherre/Code/pgsql/install/master/lib/pgxs/src/makefiles/pgxs.mk
CONFIGURE = '--enable-debug' '--enable-depend' '--enable-cassert' '--enable-nls' '--cache-file=/home/alvherre/tmp/pgconfig.master.cache' '--enable-thread-safety' '--with-python' '--with-perl' '--with-tcl' '--with-openssl' '--with-libxml' '--with-tclconfig=/usr/lib/tcl8.6' '--enable-tap-tests' '--prefix=/pgsql/install/master' '--with-pgport=55432'
CC = gcc
CPPFLAGS = -D_GNU_SOURCE -I/usr/include/libxml2
CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2
CFLAGS_SL = -fpic
LDFLAGS = -L../../../src/common -Wl,--as-needed -Wl,-rpath,'/pgsql/install/master/lib',--enable-new-dtags
LDFLAGS_EX =
LDFLAGS_SL =
LIBS = -lpgcommon -lpgport -lxml2 -lssl -lcrypto -lz -lreadline -lrt -lcrypt -ldl -lm
VERSION = PostgreSQL 9.6devel

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Robert Haas
robertmhaas@gmail.com
In reply to: Joe Conway (#18)
Re: exposing pg_controldata and pg_config as functions

On Wed, Sep 2, 2015 at 5:31 PM, Joe Conway <mail@joeconway.com> wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09/02/2015 05:25 PM, Tom Lane wrote:

Robert Haas <robertmhaas@gmail.com> writes:

But I'm not sure I like the idea of adding a server dependency on
the ability to exec pg_controldata. That seems like it could be
unreliable at best, and a security vulnerability at worst.

I hadn't been paying attention --- the proposed patch actually
depends on exec'ing pg_controldata? That's horrid! There is no
expectation that that's installed.

No it doesn't. I'm confused :-/

No, I'm confused. Sorry. Somehow I misread your patch.

Pay no attention to that man behind the curtain.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Joe Conway
mail@joeconway.com
In reply to: Alvaro Herrera (#21)
Re: exposing pg_controldata and pg_config as functions

On 09/02/2015 02:54 PM, Alvaro Herrera wrote:

Josh Berkus wrote:

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

I was primarily looking at pg_controldata, so we agree there.

As for pg_config, I'm confused about its usefulness -- which of these
lines are useful in the SQL interface? Anyway, I don't see anything
better than a couple of text columns for this case.

There are production environments where even the superuser has no
direct, local, command line access on production database servers (short
of intentional hacking, which would be frowned upon at best), and the
only interface for getting information from postgres is via a database
connection. So to the extent pg_config and pg_controldata command line
binaries are useful, so is the ability to get the same output via SQL.

Given that, my own feeling is that if we provide a SQL interface at all,
it ought to be pretty much the exact same output as the command line
programs produce.

To the extent that we want specific pg_controldata output in non-text
form, we should identify which items those are and provide individual
functions for them.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#24Peter Eisentraut
peter_e@gmx.net
In reply to: Joe Conway (#23)
Re: exposing pg_controldata and pg_config as functions

On 9/6/15 3:34 PM, Joe Conway wrote:

On 09/02/2015 02:54 PM, Alvaro Herrera wrote:

Josh Berkus wrote:

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

I was primarily looking at pg_controldata, so we agree there.

As for pg_config, I'm confused about its usefulness -- which of these
lines are useful in the SQL interface? Anyway, I don't see anything
better than a couple of text columns for this case.

There are production environments where even the superuser has no
direct, local, command line access on production database servers

But then they also have no use for the information that pg_config prints
out.

and the
only interface for getting information from postgres is via a database
connection. So to the extent pg_config and pg_controldata command line
binaries are useful, so is the ability to get the same output via SQL.

Given that, my own feeling is that if we provide a SQL interface at all,
it ought to be pretty much the exact same output as the command line
programs produce.

That argument makes no sense to me.

Again, we need to think about what actual use there is for this
information. Just because the information exists somewhere, but you
can't access it, doesn't mean we just need to copy it around.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Eisentraut (#24)
Re: exposing pg_controldata and pg_config as functions

On 09/06/2015 11:17 PM, Peter Eisentraut wrote:

On 9/6/15 3:34 PM, Joe Conway wrote:

On 09/02/2015 02:54 PM, Alvaro Herrera wrote:

Josh Berkus wrote:

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

I was primarily looking at pg_controldata, so we agree there.

As for pg_config, I'm confused about its usefulness -- which of these
lines are useful in the SQL interface? Anyway, I don't see anything
better than a couple of text columns for this case.

There are production environments where even the superuser has no
direct, local, command line access on production database servers

But then they also have no use for the information that pg_config prints
out.

and the
only interface for getting information from postgres is via a database
connection. So to the extent pg_config and pg_controldata command line
binaries are useful, so is the ability to get the same output via SQL.

Given that, my own feeling is that if we provide a SQL interface at all,
it ought to be pretty much the exact same output as the command line
programs produce.

That argument makes no sense to me.

Again, we need to think about what actual use there is for this
information. Just because the information exists somewhere, but you
can't access it, doesn't mean we just need to copy it around.

I already gave a use case that you dismissed in favour of a vague
solution that we don't actually have. You seem to be the only person
objecting to this proposal.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Andrew Dunstan (#25)
Re: exposing pg_controldata and pg_config as functions

Andrew Dunstan wrote:

I already gave a use case that you dismissed in favour of a vague solution
that we don't actually have. You seem to be the only person objecting to
this proposal.

I think that use case would be better served by a completely different
interface -- some way to query the server, "does this installation
support feature X?" What you proposed, using a regexp to look for
--enable-xml in the pg_config --configure output, doesn't look all that
nice to me.

For instance, we already have sql_features.txt, which is exposed as a
table in the docs. It's not quite the same thing (because what you want
is not the same as conformance to the SQL standard), but I think a
system view that has a list of features and a boolean flag for each one
is more practical (though admittedly it's more work to implement.)
Another alternative which I think is simpler is a read-only GUC, which
we already have for a number of compile-time properties.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Josh Berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#5)
Re: exposing pg_controldata and pg_config as functions

On 09/06/2015 12:34 PM, Joe Conway wrote:

To the extent that we want specific pg_controldata output in non-text
form, we should identify which items those are and provide individual
functions for them.

Well, I think it's pretty simple, let's take it down:

# function pg_control_control_data() returning INT, TSTZ
pg_control version number: 942
pg_control last modified: Thu 20 Aug 2015 10:05:33 AM PDT

#function pg_catversion() returning BIGINT
Catalog version number: 201409291

# have function for this, no?
Database system identifier: 6102142380557650900

# not relevant, if we can connect, it's running
Database cluster state: shut down

# Do we have functions for all of the below?
# if not I suggest virtual table pg_checkpoint_status
# returning the below 17 columns
# that would be useful even if we have some of them
Latest checkpoint location: 0/1A1BF178
Prior checkpoint location: 0/1A1BF0D8
Latest checkpoint's REDO location: 0/1A1BF178
Latest checkpoint's REDO WAL file: 00000001000000000000001A
Latest checkpoint's TimeLineID: 1
Latest checkpoint's PrevTimeLineID: 1
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID: 0/2038
Latest checkpoint's NextOID: 19684
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 711
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's oldestActiveXID: 0
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 1
Time of latest checkpoint: Thu 20 Aug 2015 10:05:33 AM PDT

# Not in any way useful
Fake LSN counter for unlogged rels: 0/1

# add another system view,
# pg_recovery_state, holding the
# below 5 columns
Minimum recovery ending location: 0/0
Min recovery ending loc's timeline: 0
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no

# duplicates system settings, not needed
Current wal_level setting: logical
Current wal_log_hints setting: off
Current max_connections setting: 100
Current max_worker_processes setting: 8
Current max_prepared_xacts setting: 0
Current max_locks_per_xact setting: 64
Maximum data alignment: 8
Database block size: 8192

# do we have the below anywhere else?
# this is somewhat duplicative of config info
Blocks per segment of large relation: 131072
WAL block size: 8192
Bytes per WAL segment: 16777216
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float4 argument passing: by value
Float8 argument passing: by value

# return INT function pg_data_page_checksum_version()
Data page checksum version: 0

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28Robert Haas
robertmhaas@gmail.com
In reply to: Joe Conway (#23)
Re: exposing pg_controldata and pg_config as functions

On Sun, Sep 6, 2015 at 3:34 PM, Joe Conway <mail@joeconway.com> wrote:

On 09/02/2015 02:54 PM, Alvaro Herrera wrote:

Josh Berkus wrote:

On 09/02/2015 02:34 PM, Alvaro Herrera wrote:

I think trying to duplicate the exact strings isn't too nice an
interface.

Well, for pg_controldata, no, but what else would you do for pg_config?

I was primarily looking at pg_controldata, so we agree there.

As for pg_config, I'm confused about its usefulness -- which of these
lines are useful in the SQL interface? Anyway, I don't see anything
better than a couple of text columns for this case.

There are production environments where even the superuser has no
direct, local, command line access on production database servers (short
of intentional hacking, which would be frowned upon at best), and the
only interface for getting information from postgres is via a database
connection. So to the extent pg_config and pg_controldata command line
binaries are useful, so is the ability to get the same output via SQL.

I don't buy that argument as far as pg_config is concerned. That's
mostly providing local pathnames. If you don't have command-line
access to the box where the server is running, you not only can't use
that information, but you probably aren't really entitled to it.

I see exposing the pg_controldata information as reasonable because
that's actually facts about the database cluster, rather than the
system on which it is running.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Peter Eisentraut
peter_e@gmx.net
In reply to: Alvaro Herrera (#26)
Re: exposing pg_controldata and pg_config as functions

On 9/7/15 7:32 PM, Alvaro Herrera wrote:

Andrew Dunstan wrote:

I already gave a use case that you dismissed in favour of a vague solution
that we don't actually have. You seem to be the only person objecting to
this proposal.

I think that use case would be better served by a completely different
interface -- some way to query the server, "does this installation
support feature X?" What you proposed, using a regexp to look for
--enable-xml in the pg_config --configure output, doesn't look all that
nice to me.

Agreed.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30Peter Eisentraut
peter_e@gmx.net
In reply to: Andrew Dunstan (#25)
Re: exposing pg_controldata and pg_config as functions

On 9/7/15 7:21 PM, Andrew Dunstan wrote:

I already gave a use case that you dismissed in favour of a vague
solution that we don't actually have.

But that's also the only use case so far, which seems a little thin.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Andrew Dunstan
andrew@dunslane.net
In reply to: Peter Eisentraut (#29)
Re: exposing pg_controldata and pg_config as functions

On 09/08/2015 04:21 PM, Peter Eisentraut wrote:

On 9/7/15 7:32 PM, Alvaro Herrera wrote:

Andrew Dunstan wrote:

I already gave a use case that you dismissed in favour of a vague solution
that we don't actually have. You seem to be the only person objecting to
this proposal.

I think that use case would be better served by a completely different
interface -- some way to query the server, "does this installation
support feature X?" What you proposed, using a regexp to look for
--enable-xml in the pg_config --configure output, doesn't look all that
nice to me.

Agreed.

The problem is that at least this user's system had something odd about
it. so that I wouldn't entirely trust the output of

select is_supported
from information_schema.sql_features
where feature_name = 'XML type';

to reflect the config.

I also have cases where clients don't want to give me superuser access,
and sometimes not even shell access, and it could well be useful to me
to be able to say to them "OK, you need to make sure that this file in
this location has this entry".

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#32Peter Eisentraut
peter_e@gmx.net
In reply to: Andrew Dunstan (#31)
Re: exposing pg_controldata and pg_config as functions

On 9/8/15 4:56 PM, Andrew Dunstan wrote:

The problem is that at least this user's system had something odd about
it. so that I wouldn't entirely trust the output of

select is_supported
from information_schema.sql_features
where feature_name = 'XML type';

to reflect the config.

This should be a built-in function, not dependent on the state of the
catalogs, like pg_build_option('xml') returns boolean.

I also have cases where clients don't want to give me superuser access,
and sometimes not even shell access, and it could well be useful to me
to be able to say to them "OK, you need to make sure that this file in
this location has this entry".

Not sure what this has to do with this.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Andres Freund
andres@anarazel.de
In reply to: Andrew Dunstan (#1)
Re: exposing pg_controldata and pg_config as functions

On 2015-08-20 09:59:25 -0400, Andrew Dunstan wrote:

Is there any significant interest in either of these?

Josh Berkus tells me that he would like pg_controldata information, and I
was a bit interested in pg_config information, for this reason: I had a
report of someone who had configured using --with-libxml but the xml tests
actually returned the results that are expected without xml being
configured. The regression tests thus passed, but should not have. It
occurred to me that if we had a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

On my reading of the thread there seems to be a tenative agreement that
pg_controldata is useful and still controversy around pg_config. Can we
split committing this?

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#33)
Re: exposing pg_controldata and pg_config as functions

On Thu, Sep 17, 2015 at 7:12 AM, Andres Freund <andres@anarazel.de> wrote:

On 2015-08-20 09:59:25 -0400, Andrew Dunstan wrote:

Is there any significant interest in either of these?

Josh Berkus tells me that he would like pg_controldata information, and I
was a bit interested in pg_config information, for this reason: I had a
report of someone who had configured using --with-libxml but the xml tests
actually returned the results that are expected without xml being
configured. The regression tests thus passed, but should not have. It
occurred to me that if we had a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

On my reading of the thread there seems to be a tentative agreement that
pg_controldata is useful and still controversy around pg_config. Can we
split committing this?

Yeah, the last version of the patch dates of August, and there is
visibly agreement that the information of pg_controldata provided at
SQL level is useful while the data of pg_config is proving to be less
interesting for remote users. Could the patch be rebased and split as
suggested above?
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#35Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#34)
Re: exposing pg_controldata and pg_config as functions

On Wed, Dec 9, 2015 at 9:18 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Thu, Sep 17, 2015 at 7:12 AM, Andres Freund <andres@anarazel.de> wrote:

On 2015-08-20 09:59:25 -0400, Andrew Dunstan wrote:

Is there any significant interest in either of these?

Josh Berkus tells me that he would like pg_controldata information, and I
was a bit interested in pg_config information, for this reason: I had a
report of someone who had configured using --with-libxml but the xml tests
actually returned the results that are expected without xml being
configured. The regression tests thus passed, but should not have. It
occurred to me that if we had a test like

select pg_config('configure') ~ '--with-libxml' as has_xml;

in the xml tests then this failure mode would be detected.

On my reading of the thread there seems to be a tentative agreement that
pg_controldata is useful and still controversy around pg_config. Can we
split committing this?

Yeah, the last version of the patch dates of August, and there is
visibly agreement that the information of pg_controldata provided at
SQL level is useful while the data of pg_config is proving to be less
interesting for remote users. Could the patch be rebased and split as
suggested above?

I am marking this patch as returned with feedback, there is not much activity...
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#35)
Re: exposing pg_controldata and pg_config as functions

On 12/23/2015 05:45 AM, Michael Paquier wrote:

Yeah, the last version of the patch dates of August, and there is
visibly agreement that the information of pg_controldata provided at
SQL level is useful while the data of pg_config is proving to be less
interesting for remote users. Could the patch be rebased and split as
suggested above?

I am marking this patch as returned with feedback, there is not much activity...

I just dusted this off yesterday finally. Anyway, based on the
discussions I plan to:

1) split it into two separate patches, one for pg_config and one for
pg_controldata.
2) Change the pg_controldata to be a bunch of separate functions as
suggested by Josh Berkus rather than one SRF.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#37Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#36)
Re: exposing pg_controldata and pg_config as functions

On Thu, Dec 24, 2015 at 2:08 AM, Joe Conway <mail@joeconway.com> wrote:

On 12/23/2015 05:45 AM, Michael Paquier wrote:

Yeah, the last version of the patch dates of August, and there is
visibly agreement that the information of pg_controldata provided at
SQL level is useful while the data of pg_config is proving to be less
interesting for remote users. Could the patch be rebased and split as
suggested above?

I am marking this patch as returned with feedback, there is not much activity...

I just dusted this off yesterday finally. Anyway, based on the
discussions I plan to:

1) split it into two separate patches, one for pg_config and one for
pg_controldata.
2) Change the pg_controldata to be a bunch of separate functions as
suggested by Josh Berkus rather than one SRF.

This looks like a plan, thanks!
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#37)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 12/23/2015 04:37 PM, Michael Paquier wrote:

On Thu, Dec 24, 2015 at 2:08 AM, Joe Conway <mail@joeconway.com> wrote:

On 12/23/2015 05:45 AM, Michael Paquier wrote:

Yeah, the last version of the patch dates of August, and there is
visibly agreement that the information of pg_controldata provided at
SQL level is useful while the data of pg_config is proving to be less
interesting for remote users. Could the patch be rebased and split as
suggested above?

I am marking this patch as returned with feedback, there is not much activity...

I just dusted this off yesterday finally. Anyway, based on the
discussions I plan to:

1) split it into two separate patches, one for pg_config and one for
pg_controldata.
2) Change the pg_controldata to be a bunch of separate functions as
suggested by Josh Berkus rather than one SRF.

This looks like a plan, thanks!

First installment -- pg_config function/view as a separate patch,
rebased to current master.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

20151226-1-pgconfig.difftext/x-diff; name=20151226-1-pgconfig.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 536c805..a50ffe8 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..e0015d8 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,34 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! # 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 = 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
index ...e29a706 .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,266 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Portions Copyright (c) 1996-2015, 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 "utils/builtins.h"
+ #include "utils/elog.h"
+ #include "port.h"
+ 
+ static void get_configdata(void);
+ 
+ #ifdef PGDLLIMPORT
+ /* Postgres global */
+ extern PGDLLIMPORT char my_exec_path[];
+ #else
+ /* Postgres global */
+ extern DLLIMPORT char my_exec_path[];
+ #endif /* PGDLLIMPORT */
+ 
+ struct configdata
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ };
+ 
+ static struct configdata ConfigData[] =
+ {
+ 	{"BINDIR", NULL},
+ 	{"DOCDIR", NULL},
+ 	{"HTMLDIR", NULL},
+ 	{"INCLUDEDIR", NULL},
+ 	{"PKGINCLUDEDIR", NULL},
+ 	{"INCLUDEDIR-SERVER", NULL},
+ 	{"LIBDIR", NULL},
+ 	{"PKGLIBDIR", NULL},
+ 	{"LOCALEDIR", NULL},
+ 	{"MANDIR", NULL},
+ 	{"SHAREDIR", NULL},
+ 	{"SYSCONFDIR", NULL},
+ 	{"PGXS", NULL},
+ 	{"CONFIGURE", NULL},
+ 	{"CC", NULL},
+ 	{"CPPFLAGS", NULL},
+ 	{"CFLAGS", NULL},
+ 	{"CFLAGS_SL", NULL},
+ 	{"LDFLAGS", NULL},
+ 	{"LDFLAGS_EX", NULL},
+ 	{"LDFLAGS_SL", NULL},
+ 	{"LIBS", NULL},
+ 	{"VERSION", NULL},
+ 	{NULL, NULL}
+ };
+ 
+ static void get_configdata(void);
+ 
+ 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;
+ 	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);
+ 
+ 	get_configdata();
+ 	while (ConfigData[i].name)
+ 	{
+ 		values[0] = ConfigData[i].name;
+ 		values[1] = ConfigData[i].setting;
+ 
+ 		tuple = BuildTupleFromCStrings(attinmeta, values);
+ 		tuplestore_puttuple(tupstore, tuple);
+ 		++i;
+ 	}
+ 	
+ 	/*
+ 	 * 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;
+ }
+ 
+ 
+ static void
+ get_configdata(void)
+ {
+ 	char			path[MAXPGPATH];
+ 	char		   *lastsep;
+ 
+ 	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);
+ }
diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c
index 9b1f95d..25efc71 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 29,65 ****
  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
--- 29,34 ----
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..2e1a418 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5337,5342 ****
--- 5337,5346 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* pg_config */
+ DATA(insert OID = 3330 ( 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/port.h b/src/include/port.h
index 3787cbf..ab38f62 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 e610bf3..d0abb82 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1123,1128 ****
--- 1123,1131 ----
  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 d0f72df..7613666 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 80374e4..151b8c1 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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,
#39Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#37)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 12/23/2015 04:37 PM, Michael Paquier wrote:

On Thu, Dec 24, 2015 at 2:08 AM, Joe Conway <mail@joeconway.com> wrote:

2) Change the pg_controldata to be a bunch of separate functions as
suggested by Josh Berkus rather than one SRF.

This looks like a plan, thanks!

As discussed, a completely revamped and split off pg_controldata patch.
Below are the details for those interested.

Comments please.

Joe

===============
What this patch does:
---------------
1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)
2) Refactor bin/pg_controldata (there should be no visible change to
pg_controldata output)
3) Adds new functions, more or less in line with previous discussions:
* pg_checkpoint_state()
* pg_controldata_state()
* pg_recovery_state()
* pg_init_state()

===============
Missing (TODO once agreement on the above is reached):
---------------
a) documentation
b) catversion bump
c) regression tests

===============
New function detail and sample output:
---------------
postgres=# \x
Expanded display is on.

postgres=# \df pg_*_state
List of functions
-[ RECORD 1 ]-------+----------------------------------------------
Schema | pg_catalog
Name | pg_checkpoint_state
Result data type | record
Argument data types | OUT checkpoint_location pg_lsn,
| OUT prior_location pg_lsn,
| OUT redo_location pg_lsn,
| OUT redo_wal_file text,
| OUT timeline_id integer,
| OUT prev_timeline_id integer,
| OUT full_page_writes boolean,
| OUT next_xid text,
| OUT next_oid oid,
| OUT next_multixact_id xid,
| OUT next_multi_offset xid,
| OUT oldest_xid xid,
| OUT oldest_xid_dbid oid,
| OUT oldest_active_xid xid,
| OUT oldest_multi_xid xid,
| OUT oldest_multi_dbid oid,
| OUT oldest_commit_ts_xid xid,
| OUT newest_commit_ts_xid xid,
| OUT checkpoint_time timestamp with time zone
Type | normal
-[ RECORD 2 ]-------+----------------------------------------------
Schema | pg_catalog
Name | pg_controldata_state
Result data type | record
Argument data types | OUT pg_control_version integer,
| OUT catalog_version_no integer,
| OUT system_identifier bigint,
| OUT pg_control_last_modified
| timestamp with time zone
Type | normal
-[ RECORD 3 ]-------+----------------------------------------------
Schema | pg_catalog
Name | pg_init_state
Result data type | record
Argument data types | OUT max_data_alignment integer,
| OUT database_block_size integer,
| OUT blocks_per_segment integer,
| OUT wal_block_size integer,
| OUT bytes_per_wal_segment integer,
| OUT max_identifier_length integer,
| OUT max_index_columns integer,
| OUT max_toast_chunk_size integer,
| OUT large_object_chunk_size integer,
| OUT bigint_timestamps boolean,
| OUT float4_pass_by_value boolean,
| OUT float8_pass_by_value boolean,
| OUT data_page_checksum_version integer
Type | normal
-[ RECORD 4 ]-------+----------------------------------------------
Schema | pg_catalog
Name | pg_recovery_state
Result data type | record
Argument data types | OUT min_recovery_end_location pg_lsn,
| OUT min_recovery_end_timeline integer,
| OUT backup_start_location pg_lsn,
| OUT backup_end_location pg_lsn,
| OUT end_of_backup_record_required boolean
Type | normal

postgres=# select * from pg_controldata_state();
-[ RECORD 1 ]------------+-----------------------
pg_control_version | 942
catalog_version_no | 201511071
system_identifier | 6233852631805477166
pg_control_last_modified | 2015-12-29 15:32:09-08

postgres=# select * from pg_checkpoint_state();
-[ RECORD 1 ]--------+-------------------------
checkpoint_location | 0/14E8C38
prior_location | 0/14D6340
redo_location | 0/14E8C38
redo_wal_file | 000000010000000000000001
timeline_id | 1
prev_timeline_id | 1
full_page_writes | t
next_xid | 0:574
next_oid | 12407
next_multixact_id | 1
next_multi_offset | 0
oldest_xid | 565
oldest_xid_dbid | 1
oldest_active_xid | 0
oldest_multi_xid | 1
oldest_multi_dbid | 1
oldest_commit_ts_xid | 0
newest_commit_ts_xid | 0
checkpoint_time | 2015-12-29 15:32:02-08

postgres=# select * from pg_recovery_state();
-[ RECORD 1 ]-----------------+----
min_recovery_end_location | 0/0
min_recovery_end_timeline | 0
backup_start_location | 0/0
backup_end_location | 0/0
end_of_backup_record_required | f

postgres=# select * from pg_init_state();
-[ RECORD 1 ]--------------+---------
max_data_alignment | 8
database_block_size | 8192
blocks_per_segment | 131072
wal_block_size | 8192
bytes_per_wal_segment | 16777216
max_identifier_length | 64
max_index_columns | 32
max_toast_chunk_size | 1996
large_object_chunk_size | 2048
bigint_timestamps | t
float4_pass_by_value | t
float8_pass_by_value | t
data_page_checksum_version | 0

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_controldata_funcs-00.difftext/x-diff; name=pg_controldata_funcs-00.diffDownload
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 7889101..80b139e 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! OBJS = guc.o help_config.o pg_controldata.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_controldata.c b/src/backend/utils/misc/pg_controldata.c
index ...fd00ec7 .
*** a/src/backend/utils/misc/pg_controldata.c
--- b/src/backend/utils/misc/pg_controldata.c
***************
*** 0 ****
--- 1,358 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_controldata.c
+  *		Expose select pg_controldata output, except via SQL functions
+  *
+  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  *	  src/backend/utils/misc/pg_controldata.c
+  *
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "access/htup_details.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "utils/builtins.h"
+ #include "utils/pg_lsn.h"
+ #include "utils/timestamp.h"
+ 
+ extern controldata *ControlData;
+ 
+ Datum
+ pg_controldata_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[4];
+ 	bool				nulls[4];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(4, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier",
+ 					   INT8OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->catalog_version_no);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int64GetDatum(ControlFile->system_identifier);
+ 	nulls[2] = false;
+ 
+ 	values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
+ 	nulls[3] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_checkpoint_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[19];
+ 	bool				nulls[19];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(19, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* Read the control file. */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	/* Make sure it is valid. */
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/* Populate the values and null arrays */
+ 	values[0] = LSNGetDatum(ControlFile->checkPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
+ 	nulls[2] = false;
+ 
+ 	values[3] = CStringGetTextDatum(xlogfilename);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
+ 	nulls[5] = false;
+ 
+ 	values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
+ 	nulls[6] = false;
+ 
+ 	values[7] = CStringGetTextDatum(psprintf("%u:%u",
+ 								ControlFile->checkPointCopy.nextXidEpoch,
+ 								ControlFile->checkPointCopy.nextXid));
+ 	nulls[7] = false;
+ 
+ 	values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
+ 	nulls[8] = false;
+ 
+ 	values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
+ 	nulls[9] = false;
+ 
+ 	values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
+ 	nulls[10] = false;
+ 
+ 	values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
+ 	nulls[11] = false;
+ 
+ 	values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
+ 	nulls[12] = false;
+ 
+ 	values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
+ 	nulls[13] = false;
+ 
+ 	values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
+ 	nulls[14] = false;
+ 
+ 	values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
+ 	nulls[15] = false;
+ 
+ 	values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	nulls[16] = false;
+ 
+ 	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
+ 	nulls[17] = false;
+ 
+ 	values[18] = TimestampTzGetDatum(
+ 					time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+ 	nulls[18] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_recovery_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[5];
+ 	bool				nulls[5];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(5, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
+ 					   BOOLOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->backupStartPoint);
+ 	nulls[2] = false;
+ 
+ 	values[3] = LSNGetDatum(ControlFile->backupEndPoint);
+ 	nulls[3] = false;
+ 
+ 	values[4] = BoolGetDatum(ControlFile->backupEndRequired);
+ 	nulls[4] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_init_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[13];
+ 	bool				nulls[13];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(13, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "bigint_timestamps",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float4_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "float8_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "data_page_checksum_version",
+ 					   INT4OID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->maxAlign);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->blcksz);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int32GetDatum(ControlFile->relseg_size);
+ 	nulls[2] = false;
+ 
+ 	values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->nameDataLen);
+ 	nulls[5] = false;
+ 
+ 	values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
+ 	nulls[6] = false;
+ 
+ 	values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
+ 	nulls[7] = false;
+ 
+ 	values[8] = Int32GetDatum(ControlFile->loblksize);
+ 	nulls[8] = false;
+ 
+ 	values[9] = BoolGetDatum(ControlFile->enableIntTimes);
+ 	nulls[9] = false;
+ 
+ 	values[10] = BoolGetDatum(ControlFile->float4ByVal);
+ 	nulls[10] = false;
+ 
+ 	values[11] = BoolGetDatum(ControlFile->float8ByVal);
+ 	nulls[11] = false;
+ 
+ 	values[12] = Int32GetDatum(ControlFile->data_checksum_version);
+ 	nulls[12] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index e7e072f..75ed71b 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
***************
*** 26,31 ****
--- 26,32 ----
  #include "access/xlog.h"
  #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
  #include "pg_getopt.h"
  
  
*************** usage(const char *progname)
*** 45,108 ****
  }
  
  
! static const char *
! dbState(DBState state)
! {
! 	switch (state)
! 	{
! 		case DB_STARTUP:
! 			return _("starting up");
! 		case DB_SHUTDOWNED:
! 			return _("shut down");
! 		case DB_SHUTDOWNED_IN_RECOVERY:
! 			return _("shut down in recovery");
! 		case DB_SHUTDOWNING:
! 			return _("shutting down");
! 		case DB_IN_CRASH_RECOVERY:
! 			return _("in crash recovery");
! 		case DB_IN_ARCHIVE_RECOVERY:
! 			return _("in archive recovery");
! 		case DB_IN_PRODUCTION:
! 			return _("in production");
! 	}
! 	return _("unrecognized status code");
! }
! 
! static const char *
! wal_level_str(WalLevel wal_level)
! {
! 	switch (wal_level)
! 	{
! 		case WAL_LEVEL_MINIMAL:
! 			return "minimal";
! 		case WAL_LEVEL_ARCHIVE:
! 			return "archive";
! 		case WAL_LEVEL_HOT_STANDBY:
! 			return "hot_standby";
! 		case WAL_LEVEL_LOGICAL:
! 			return "logical";
! 	}
! 	return _("unrecognized wal_level");
! }
! 
! 
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData ControlFile;
! 	int			fd;
! 	char		ControlFilePath[MAXPGPATH];
! 	char	   *DataDir = NULL;
! 	pg_crc32c	crc;
! 	time_t		time_tmp;
! 	char		pgctime_str[128];
! 	char		ckpttime_str[128];
! 	char		sysident_str[32];
! 	const char *strftime_fmt = "%c";
! 	const char *progname;
! 	XLogSegNo	segno;
! 	char		xlogfilename[MAXFNAMELEN];
! 	int			c;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata"));
  
--- 46,62 ----
  }
  
  
! #define CONTROLDATANAME_LEN 39
! #define CONTROLDATANAME_FMT "%-39s%s\n"
  int
  main(int argc, char *argv[])
  {
! 	char		   *DataDir = NULL;
! 	controldata	   *ControlData;
! 	size_t			ControlData_len;
! 	char			ControlDataName[CONTROLDATANAME_LEN + 1];
! 	const char	   *progname;
! 	int				c, i;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata"));
  
*************** main(int argc, char *argv[])
*** 161,338 ****
  		exit(1);
  	}
  
! 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
! 
! 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
! 	{
! 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 
! 	if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
  	{
! 		fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
  	}
- 	close(fd);
- 
- 	/* Check the CRC. */
- 	INIT_CRC32C(crc);
- 	COMP_CRC32C(crc,
- 				(char *) &ControlFile,
- 				offsetof(ControlFileData, crc));
- 	FIN_CRC32C(crc);
- 
- 	if (!EQ_CRC32C(crc, ControlFile.crc))
- 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
- 				 "Either the file is corrupt, or it has a different layout than this program\n"
- 				 "is expecting.  The results below are untrustworthy.\n\n"));
- 
- 	/*
- 	 * This slightly-chintzy coding will work as long as the control file
- 	 * timestamps are within the range of time_t; that should be the case in
- 	 * all foreseeable circumstances, so we don't bother importing the
- 	 * backend's timezone library into pg_controldata.
- 	 *
- 	 * Use variable for format to suppress overly-anal-retentive gcc warning
- 	 * about %c
- 	 */
- 	time_tmp = (time_t) ControlFile.time;
- 	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
- 			 localtime(&time_tmp));
- 	time_tmp = (time_t) ControlFile.checkPointCopy.time;
- 	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
- 			 localtime(&time_tmp));
- 
- 	/*
- 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
- 	 * start point.
- 	 */
- 	XLByteToSeg(ControlFile.checkPointCopy.redo, segno);
- 	XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno);
- 
- 	/*
- 	 * Format system_identifier separately to keep platform-dependent format
- 	 * code out of the translatable message string.
- 	 */
- 	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
- 			 ControlFile.system_identifier);
  
- 	printf(_("pg_control version number:            %u\n"),
- 		   ControlFile.pg_control_version);
- 	if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0)
- 		printf(_("WARNING: possible byte ordering mismatch\n"
- 				 "The byte ordering used to store the pg_control file might not match the one\n"
- 				 "used by this program.  In that case the results below would be incorrect, and\n"
- 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
- 	printf(_("Catalog version number:               %u\n"),
- 		   ControlFile.catalog_version_no);
- 	printf(_("Database system identifier:           %s\n"),
- 		   sysident_str);
- 	printf(_("Database cluster state:               %s\n"),
- 		   dbState(ControlFile.state));
- 	printf(_("pg_control last modified:             %s\n"),
- 		   pgctime_str);
- 	printf(_("Latest checkpoint location:           %X/%X\n"),
- 		   (uint32) (ControlFile.checkPoint >> 32),
- 		   (uint32) ControlFile.checkPoint);
- 	printf(_("Prior checkpoint location:            %X/%X\n"),
- 		   (uint32) (ControlFile.prevCheckPoint >> 32),
- 		   (uint32) ControlFile.prevCheckPoint);
- 	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
- 		   (uint32) (ControlFile.checkPointCopy.redo >> 32),
- 		   (uint32) ControlFile.checkPointCopy.redo);
- 	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
- 		   xlogfilename);
- 	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
- 		   ControlFile.checkPointCopy.ThisTimeLineID);
- 	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
- 		   ControlFile.checkPointCopy.PrevTimeLineID);
- 	printf(_("Latest checkpoint's full_page_writes: %s\n"),
- 		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
- 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
- 		   ControlFile.checkPointCopy.nextXidEpoch,
- 		   ControlFile.checkPointCopy.nextXid);
- 	printf(_("Latest checkpoint's NextOID:          %u\n"),
- 		   ControlFile.checkPointCopy.nextOid);
- 	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
- 		   ControlFile.checkPointCopy.nextMulti);
- 	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
- 		   ControlFile.checkPointCopy.nextMultiOffset);
- 	printf(_("Latest checkpoint's oldestXID:        %u\n"),
- 		   ControlFile.checkPointCopy.oldestXid);
- 	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
- 		   ControlFile.checkPointCopy.oldestXidDB);
- 	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
- 		   ControlFile.checkPointCopy.oldestActiveXid);
- 	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
- 		   ControlFile.checkPointCopy.oldestMulti);
- 	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
- 		   ControlFile.checkPointCopy.oldestMultiDB);
- 	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
- 		   ControlFile.checkPointCopy.oldestCommitTsXid);
- 	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
- 		   ControlFile.checkPointCopy.newestCommitTsXid);
- 	printf(_("Time of latest checkpoint:            %s\n"),
- 		   ckpttime_str);
- 	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
- 		   (uint32) (ControlFile.unloggedLSN >> 32),
- 		   (uint32) ControlFile.unloggedLSN);
- 	printf(_("Minimum recovery ending location:     %X/%X\n"),
- 		   (uint32) (ControlFile.minRecoveryPoint >> 32),
- 		   (uint32) ControlFile.minRecoveryPoint);
- 	printf(_("Min recovery ending loc's timeline:   %u\n"),
- 		   ControlFile.minRecoveryPointTLI);
- 	printf(_("Backup start location:                %X/%X\n"),
- 		   (uint32) (ControlFile.backupStartPoint >> 32),
- 		   (uint32) ControlFile.backupStartPoint);
- 	printf(_("Backup end location:                  %X/%X\n"),
- 		   (uint32) (ControlFile.backupEndPoint >> 32),
- 		   (uint32) ControlFile.backupEndPoint);
- 	printf(_("End-of-backup record required:        %s\n"),
- 		   ControlFile.backupEndRequired ? _("yes") : _("no"));
- 	printf(_("wal_level setting:                    %s\n"),
- 		   wal_level_str(ControlFile.wal_level));
- 	printf(_("wal_log_hints setting:                %s\n"),
- 		   ControlFile.wal_log_hints ? _("on") : _("off"));
- 	printf(_("max_connections setting:              %d\n"),
- 		   ControlFile.MaxConnections);
- 	printf(_("max_worker_processes setting:         %d\n"),
- 		   ControlFile.max_worker_processes);
- 	printf(_("max_prepared_xacts setting:           %d\n"),
- 		   ControlFile.max_prepared_xacts);
- 	printf(_("max_locks_per_xact setting:           %d\n"),
- 		   ControlFile.max_locks_per_xact);
- 	printf(_("track_commit_timestamp setting:       %s\n"),
- 		   ControlFile.track_commit_timestamp ? _("on") : _("off"));
- 	printf(_("Maximum data alignment:               %u\n"),
- 		   ControlFile.maxAlign);
- 	/* we don't print floatFormat since can't say much useful about it */
- 	printf(_("Database block size:                  %u\n"),
- 		   ControlFile.blcksz);
- 	printf(_("Blocks per segment of large relation: %u\n"),
- 		   ControlFile.relseg_size);
- 	printf(_("WAL block size:                       %u\n"),
- 		   ControlFile.xlog_blcksz);
- 	printf(_("Bytes per WAL segment:                %u\n"),
- 		   ControlFile.xlog_seg_size);
- 	printf(_("Maximum length of identifiers:        %u\n"),
- 		   ControlFile.nameDataLen);
- 	printf(_("Maximum columns in an index:          %u\n"),
- 		   ControlFile.indexMaxKeys);
- 	printf(_("Maximum size of a TOAST chunk:        %u\n"),
- 		   ControlFile.toast_max_chunk_size);
- 	printf(_("Size of a large-object chunk:         %u\n"),
- 		   ControlFile.loblksize);
- 	printf(_("Date/time type storage:               %s\n"),
- 		   (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
- 	printf(_("Float4 argument passing:              %s\n"),
- 		   (ControlFile.float4ByVal ? _("by value") : _("by reference")));
- 	printf(_("Float8 argument passing:              %s\n"),
- 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
- 	printf(_("Data page checksum version:           %u\n"),
- 		   ControlFile.data_checksum_version);
  	return 0;
  }
--- 115,128 ----
  		exit(1);
  	}
  
! 	ControlData = get_controldata(DataDir, progname, &ControlData_len);
! 	for (i = 0; i < ControlData_len; i++)
  	{
! 		memset(ControlDataName, ' ', CONTROLDATANAME_LEN);
! 		snprintf (ControlDataName, CONTROLDATANAME_LEN,
! 				  "%s:", ControlData[i].name);
! 		printf(CONTROLDATANAME_FMT, ControlDataName, ControlData[i].setting);
  	}
  
  	return 0;
  }
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index f675c92..642ec54 100644
*** a/src/bin/pg_resetxlog/pg_resetxlog.c
--- b/src/bin/pg_resetxlog/pg_resetxlog.c
*************** PrintControlValues(bool guessed)
*** 646,652 ****
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..5d04a6b 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,30 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! OBJS_COMMON = controldata_utils.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
  
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index ...f7b12c8 .
*** a/src/common/controldata_utils.c
--- b/src/common/controldata_utils.c
***************
*** 0 ****
--- 1,378 ----
+ /*-------------------------------------------------------------------------
+  *
+  * controldata_utils.c
+  *		Common code for pg_controldata output
+  *
+  *
+  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include <unistd.h>
+ #include <time.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ 
+ #include "miscadmin.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "port/pg_crc32c.h"
+ #include "utils/builtins.h"
+ 
+ #ifndef FRONTEND
+ /* NOTE: caller must provide gettext call around str */
+ #define log_error(str, param1, param2)	\
+ 	elog(ERROR, str, param1, param2)
+ #else
+ #define log_error(str, param1, param2)	\
+ 	do { \
+ 			char *buf = psprintf("%%s: %s", str); \
+ 			fprintf(stderr, buf, progname, param1, param2); \
+ 			exit(2); \
+ 	} while (0)
+ #endif
+ 
+ static const char *dbState(DBState state);
+ static const char *wal_level_str(WalLevel wal_level);
+ 
+ static const char *const ControlDataNames[] =
+ {
+ 	gettext_noop("pg_control version number"),
+ 	gettext_noop("Catalog version number"),
+ 	gettext_noop("Database system identifier"),
+ 	gettext_noop("Database cluster state"),
+ 	gettext_noop("pg_control last modified"),
+ 	gettext_noop("Latest checkpoint location"),
+ 	gettext_noop("Prior checkpoint location"),
+ 	gettext_noop("Latest checkpoint's REDO location"),
+ 	gettext_noop("Latest checkpoint's REDO WAL file"),
+ 	gettext_noop("Latest checkpoint's TimeLineID"),
+ 	gettext_noop("Latest checkpoint's PrevTimeLineID"),
+ 	gettext_noop("Latest checkpoint's full_page_writes"),
+ 	gettext_noop("Latest checkpoint's NextXID"),
+ 	gettext_noop("Latest checkpoint's NextOID"),
+ 	gettext_noop("Latest checkpoint's NextMultiXactId"),
+ 	gettext_noop("Latest checkpoint's NextMultiOffset"),
+ 	gettext_noop("Latest checkpoint's oldestXID"),
+ 	gettext_noop("Latest checkpoint's oldestXID's DB"),
+ 	gettext_noop("Latest checkpoint's oldestActiveXID"),
+ 	gettext_noop("Latest checkpoint's oldestMultiXid"),
+ 	gettext_noop("Latest checkpoint's oldestMulti's DB"),
+ 	gettext_noop("Latest checkpoint's oldestCommitTsXid"),
+ 	gettext_noop("Latest checkpoint's newestCommitTsXid"),
+ 	gettext_noop("Time of latest checkpoint"),
+ 	gettext_noop("Fake LSN counter for unlogged rels"),
+ 	gettext_noop("Minimum recovery ending location"),
+ 	gettext_noop("Min recovery ending loc's timeline"),
+ 	gettext_noop("Backup start location"),
+ 	gettext_noop("Backup end location"),
+ 	gettext_noop("End-of-backup record required"),
+ 	gettext_noop("wal_level setting"),
+ 	gettext_noop("wal_log_hints setting"),
+ 	gettext_noop("max_connections setting"),
+ 	gettext_noop("max_worker_processes setting"),
+ 	gettext_noop("max_prepared_xacts setting"),
+ 	gettext_noop("max_locks_per_xact setting"),
+ 	gettext_noop("track_commit_timestamp setting"),
+ 	gettext_noop("Maximum data alignment"),
+ 	gettext_noop("Database block size"),
+ 	gettext_noop("Blocks per segment of large relation"),
+ 	gettext_noop("WAL block size"),
+ 	gettext_noop("Bytes per WAL segment"),
+ 	gettext_noop("Maximum length of identifiers"),
+ 	gettext_noop("Maximum columns in an index"),
+ 	gettext_noop("Maximum size of a TOAST chunk"),
+ 	gettext_noop("Size of a large-object chunk"),
+ 	gettext_noop("Date/time type storage"),
+ 	gettext_noop("Float4 argument passing"),
+ 	gettext_noop("Float8 argument passing"),
+ 	gettext_noop("Data page checksum version"),
+ 	NULL
+ };
+ 
+ static size_t
+ ControlDataNames_len(void)
+ {
+ 	size_t	i = 0;
+ 
+ 	while (ControlDataNames[i])
+ 		i++;
+ 
+ 	return i;
+ }
+ 
+ 
+ static const char *
+ dbState(DBState state)
+ {
+ 	switch (state)
+ 	{
+ 		case DB_STARTUP:
+ 			return _("starting up");
+ 		case DB_SHUTDOWNED:
+ 			return _("shut down");
+ 		case DB_SHUTDOWNED_IN_RECOVERY:
+ 			return _("shut down in recovery");
+ 		case DB_SHUTDOWNING:
+ 			return _("shutting down");
+ 		case DB_IN_CRASH_RECOVERY:
+ 			return _("in crash recovery");
+ 		case DB_IN_ARCHIVE_RECOVERY:
+ 			return _("in archive recovery");
+ 		case DB_IN_PRODUCTION:
+ 			return _("in production");
+ 	}
+ 	return _("unrecognized status code");
+ }
+ 
+ static const char *
+ wal_level_str(WalLevel wal_level)
+ {
+ 	switch (wal_level)
+ 	{
+ 		case WAL_LEVEL_MINIMAL:
+ 			return "minimal";
+ 		case WAL_LEVEL_ARCHIVE:
+ 			return "archive";
+ 		case WAL_LEVEL_HOT_STANDBY:
+ 			return "hot_standby";
+ 		case WAL_LEVEL_LOGICAL:
+ 			return "logical";
+ 	}
+ 	return _("unrecognized wal_level");
+ }
+ 
+ 
+ ControlFileData *
+ get_controlfile(char *DataDir, const char *progname)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	int					fd;
+ 	char				ControlFilePath[MAXPGPATH];
+ 	pg_crc32c			crc;
+ 
+ 	ControlFile = palloc(sizeof(ControlFileData));
+ 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+ 
+ 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+ 		log_error(_("could not open file \"%s\" for reading: %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+ 		log_error(_("could not read file \"%s\": %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	close(fd);
+ 
+ 	/* Check the CRC. */
+ 	INIT_CRC32C(crc);
+ 	COMP_CRC32C(crc,
+ 			   (char *) ControlFile,
+ 			   offsetof(ControlFileData, crc));
+ 	FIN_CRC32C(crc);
+ 
+ 	if (!EQ_CRC32C(crc, ControlFile->crc))
+ #ifndef FRONTEND
+ 		elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
+ #else
+ 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+ 				 "Either the file is corrupt, or it has a different layout than this program\n"
+ 				 "is expecting.  The results below are untrustworthy.\n\n"));
+ #endif
+ 
+ 	return ControlFile;
+ }
+ 
+ controldata *
+ get_controldata(char *DataDir, const char *progname,
+ 				size_t *controldata_len)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	controldata		   *ControlData;
+ 	time_t				time_tmp;
+ 	char				pgctime_str[128];
+ 	char				ckpttime_str[128];
+ 	char				sysident_str[32];
+ 	const char		   *strftime_fmt = "%c";
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 	int					i;
+ 	int					idx = 0;
+ 
+ 	*controldata_len = ControlDataNames_len();
+ 	ControlData = palloc(*controldata_len * sizeof(controldata));
+ 
+ 	/*
+ 	 * initialize controldata names
+ 	 *
+ 	 * These better be in sync with the settings manually
+ 	 * defined below.
+ 	 */
+ 	for (i = 0; i < *controldata_len; i++)
+ 		ControlData[i].name = pstrdup(_(ControlDataNames[i]));
+ 
+ 	/* get a copy of the control file */
+ 	ControlFile = get_controlfile(DataDir, progname);
+ 
+ 	/*
+ 	 * This slightly-chintzy coding will work as long as the control file
+ 	 * timestamps are within the range of time_t; that should be the case in
+ 	 * all foreseeable circumstances, so we don't bother importing the
+ 	 * backend's timezone library.
+ 	 *
+ 	 * Use variable for format to suppress overly-anal-retentive gcc warning
+ 	 * about %c
+ 	 */
+ 	time_tmp = (time_t) ControlFile->time;
+ 	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 	time_tmp = (time_t) ControlFile->checkPointCopy.time;
+ 	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/*
+ 	 * Format system_identifier separately to keep platform-dependent format
+ 	 * code out of the translatable message string.
+ 	 */
+ 	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
+ 			 ControlFile->system_identifier);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ #ifndef FRONTEND
+ 		elog(ERROR, _("byte ordering mismatch"));
+ #else
+ 		printf(_("WARNING: possible byte ordering mismatch\n"
+ 				 "The byte ordering used to store the pg_control file might not match the one\n"
+ 				 "used by this program.  In that case the results below would be incorrect, and\n"
+ 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
+ #endif
+ 
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->pg_control_version);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->catalog_version_no);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		sysident_str);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		dbState(ControlFile->state));
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		pgctime_str);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->checkPoint >> 32),
+ 		(uint32) ControlFile->checkPoint);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->prevCheckPoint >> 32),
+ 		(uint32) ControlFile->prevCheckPoint);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->checkPointCopy.redo >> 32),
+ 		(uint32) ControlFile->checkPointCopy.redo);
+ 	ControlData[idx++].setting = psprintf("%s", xlogfilename);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.ThisTimeLineID);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.PrevTimeLineID);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
+ 	ControlData[idx++].setting = psprintf("%u:%u",
+ 		ControlFile->checkPointCopy.nextXidEpoch,
+ 		ControlFile->checkPointCopy.nextXid);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextOid);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextMulti);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextMultiOffset);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestXid);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestXidDB);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestActiveXid);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestMulti);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestMultiDB);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.newestCommitTsXid);
+ 	ControlData[idx++].setting = psprintf("%s", ckpttime_str);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->unloggedLSN >> 32),
+ 		(uint32) ControlFile->unloggedLSN);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->minRecoveryPoint >> 32),
+ 		(uint32) ControlFile->minRecoveryPoint);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->minRecoveryPointTLI);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->backupStartPoint >> 32),
+ 		(uint32) ControlFile->backupStartPoint);
+ 	ControlData[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->backupEndPoint >> 32),
+ 		(uint32) ControlFile->backupEndPoint);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		ControlFile->backupEndRequired ? _("yes") : _("no"));
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		wal_level_str(ControlFile->wal_level));
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		ControlFile->wal_log_hints ? _("on") : _("off"));
+ 	ControlData[idx++].setting = psprintf("%d",
+ 		ControlFile->MaxConnections);
+ 	ControlData[idx++].setting = psprintf("%d",
+ 		ControlFile->max_worker_processes);
+ 	ControlData[idx++].setting = psprintf("%d",
+ 		ControlFile->max_prepared_xacts);
+ 	ControlData[idx++].setting = psprintf("%d",
+ 		ControlFile->max_locks_per_xact);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		ControlFile->track_commit_timestamp ? _("on") : _("off"));
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->maxAlign);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->blcksz);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->relseg_size);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->xlog_blcksz);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->xlog_seg_size);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->nameDataLen);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->indexMaxKeys);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->toast_max_chunk_size);
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->loblksize);
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		(ControlFile->enableIntTimes ?
+ 		 _("64-bit integers") : _("floating-point numbers")));
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		(ControlFile->float4ByVal ? _("by value") : _("by reference")));
+ 	ControlData[idx++].setting = psprintf("%s",
+ 		(ControlFile->float8ByVal ? _("by value") : _("by reference")));
+ 	ControlData[idx++].setting = psprintf("%u",
+ 		ControlFile->data_checksum_version);
+ 
+ 	return ControlData;
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d8640db..6923136 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5337,5342 ****
--- 5337,5355 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* pg_controldata related functions */
+ DATA(insert OID = 3331 ( pg_controldata_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,20,1184}" "{o,o,o,o}" "{pg_control_version,catalog_version_no,system_identifier,pg_control_last_modified}" _null_ _null_ pg_controldata_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata general state information as a function");
+ 
+ DATA(insert OID = 3332 ( pg_checkpoint_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,3220,3220,25,23,23,16,25,26,28,28,28,26,28,28,26,28,28,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{checkpoint_location,prior_location,redo_location,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}" _null_ _null_ pg_checkpoint_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata checkpoint state information as a function");
+ 
+ DATA(insert OID = 3333 ( pg_recovery_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,23,3220,3220,16}" "{o,o,o,o,o}" "{min_recovery_end_location,min_recovery_end_timeline,backup_start_location,backup_end_location,end_of_backup_record_required}" _null_ _null_ pg_recovery_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata recovery state information as a function");
+ 
+ DATA(insert OID = 3334 ( pg_init_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_init_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata init state information 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/controldata_utils.h b/src/include/common/controldata_utils.h
index ...70989ef .
*** a/src/include/common/controldata_utils.h
--- b/src/include/common/controldata_utils.h
***************
*** 0 ****
--- 1,23 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  *	Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *	src/include/common/controldata_utils.h
+  */
+ #ifndef COMMON_CONTROLDATA_UTILS_H
+ #define COMMON_CONTROLDATA_UTILS_H
+ 
+ typedef struct controldata
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ } controldata;
+ 
+ extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+ extern controldata *get_controldata(char *DataDir, const char *progname,
+ 									size_t *controldata_len);
+ 
+ #endif   /* COMMON_CONTROLDATA_UTILS_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e610bf3..f977279 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1123,1128 ****
--- 1123,1134 ----
  extern Datum show_all_settings(PG_FUNCTION_ARGS);
  extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
  
+ /* pg_controldata.c */
+ extern Datum pg_controldata_state(PG_FUNCTION_ARGS);
+ extern Datum pg_checkpoint_state(PG_FUNCTION_ARGS);
+ extern Datum pg_recovery_state(PG_FUNCTION_ARGS);
+ extern Datum pg_init_state(PG_FUNCTION_ARGS);
+ 
  /* rls.c */
  extern Datum row_security_active(PG_FUNCTION_ARGS);
  extern Datum row_security_active_name(PG_FUNCTION_ARGS);
#40Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#39)
Re: exposing pg_controldata and pg_config as functions

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
             ControlFile.checkPointCopy.nextXidEpoch,
             ControlFile.checkPointCopy.nextXid);
      printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
             ControlFile.checkPointCopy.ThisTimeLineID);
      printf(_("Latest checkpoint's full_page_writes: %s\n"),
             ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

2) Refactor bin/pg_controldata (there should be no visible change to
pg_controldata output)
3) Adds new functions, more or less in line with previous discussions:
* pg_checkpoint_state()
* pg_controldata_state()
* pg_recovery_state()
* pg_init_state()

Taking the opposite direction of Josh upthread, why is this split
actually necessary? Isn't the idea to provide a SQL interface of what
pg_controldata shows? If this split proves to be useful, shouldn't we
do it as well for pg_controldata?

===============
Missing (TODO once agreement on the above is reached):
---------------
a) documentation

This would be good to have.

b) catversion bump

That's committer work.

c) regression tests

Hm, what would be the value of those tests? I think we could live
without for simple functions like that honestly.

I think that those functions should be superuser-only. They provide
information about the system globally.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#38)
Re: exposing pg_controldata and pg_config as functions

On Sun, Dec 27, 2015 at 5:39 AM, Joe Conway <mail@joeconway.com> wrote:

First installment -- pg_config function/view as a separate patch,
rebased to current master.

Documentation would be good to have.

! # 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)\""
This duplication from src/bin/pg_config is a bad idea. Couldn't we do
something in src/common instead that sets up values at compilation
time in a routine (perhaps set of routines) available for both the
frontend and backend?
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#41)
Re: exposing pg_controldata and pg_config as functions

On Sat, Jan 16, 2016 at 11:07 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Sun, Dec 27, 2015 at 5:39 AM, Joe Conway <mail@joeconway.com> wrote:

First installment -- pg_config function/view as a separate patch,
rebased to current master.

Documentation would be good to have.

! # 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)\""
This duplication from src/bin/pg_config is a bad idea. Couldn't we do
something in src/common instead that sets up values at compilation
time in a routine (perhaps set of routines) available for both the
frontend and backend?

Just forgot to mention that those new functions should be superuser-only.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#43Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#42)
Re: exposing pg_controldata and pg_config as functions

On Jan 16, 2016, at 9:08 AM, Michael Paquier <michael.paquier@gmail.com> wrote:

Just forgot to mention that those new functions should be superuser-only.

I think nobody should ever say this without explaining why. Superuser restrictions are necessary in some cases, but the fewer of them we have, the better.

...Robert

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#43)
Re: exposing pg_controldata and pg_config as functions

On Sun, Jan 17, 2016 at 12:27 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Jan 16, 2016, at 9:08 AM, Michael Paquier <michael.paquier@gmail.com> wrote:

Just forgot to mention that those new functions should be superuser-only.

I think nobody should ever say this without explaining why. Superuser restrictions are necessary in some cases, but the fewer of them we have, the better.

The pg_config functions are giving away information about the system
itself, isn't that potentially sensible? The pg_controdata ones show
up information about checkpoint, recovery etc. There are a couple of
fields that could be made completely visible, like the information
defined when running initdb or how the code is compiled like block
size (not the system ID), but we surely do not want to give away
checkpoint and recovery information.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#45Andres Freund
andres@anarazel.de
In reply to: Michael Paquier (#44)
Re: exposing pg_controldata and pg_config as functions

On January 17, 2016 12:46:36 AM GMT+01:00, Michael Paquier <michael.paquier@gmail.com> wrote:
, but we surely do not want to give away

checkpoint and recovery information.

Why is that? A lot of that information is available anyway?

--- 
Please excuse brevity and formatting - I am writing this on my mobile phone.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#46Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#40)
Re: exposing pg_controldata and pg_config as functions

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#47Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#41)
Re: exposing pg_controldata and pg_config as functions

On 01/16/2016 06:07 AM, Michael Paquier wrote:

On Sun, Dec 27, 2015 at 5:39 AM, Joe Conway <mail@joeconway.com> wrote:

First installment -- pg_config function/view as a separate patch,
rebased to current master.

Documentation would be good to have.

I'm definitely happy to write the docs, but earlier it was not clear
that there was enough support for this patch at all, and I don't want to
waste cycles writing docs for a feature that ultimately does not get
committed. What's the current feel for whether this feature in general
is a good idea or bad?

! # 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)\""
This duplication from src/bin/pg_config is a bad idea. Couldn't we do
something in src/common instead that sets up values at compilation
time in a routine (perhaps set of routines) available for both the
frontend and backend?

Will take a look at it.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#48Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#40)
Re: exposing pg_controldata and pg_config as functions

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

3) Adds new functions, more or less in line with previous discussions:
* pg_checkpoint_state()
* pg_controldata_state()
* pg_recovery_state()
* pg_init_state()

Taking the opposite direction of Josh upthread, why is this split
actually necessary? Isn't the idea to provide a SQL interface of what
pg_controldata shows? If this split proves to be useful, shouldn't we
do it as well for pg_controldata?

The last discussion moved strongly in the direction of separate
functions, and that being different from pg_controldata was not a bad
thing. That said, I'm still of the opinion that there are legitimate
reasons to want the command line pg_controldata and the SQL functions to
produce equivalent, if not identical, results. I just wish we could get
a clear consensus one way or the other.

===============
Missing (TODO once agreement on the above is reached):
---------------
a) documentation

This would be good to have.

Sure, once we have settled on a direction.

b) catversion bump

That's committer work.

I know, but since I will be the committer if this thing ever gets that
far, I wanted to point out that I had not forgotten that little detail ;-)

c) regression tests

Hm, what would be the value of those tests? I think we could live
without for simple functions like that honestly.

Works for me.

I think that those functions should be superuser-only. They provide
information about the system globally.

The tail of this thread seems to be headed away from this direction.
I'll take another look and propose something concrete.

Thanks for the comments!

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#49Michael Paquier
michael.paquier@gmail.com
In reply to: Andres Freund (#45)
Re: exposing pg_controldata and pg_config as functions

On Sun, Jan 17, 2016 at 8:48 AM, Andres Freund <andres@anarazel.de> wrote:

On January 17, 2016 12:46:36 AM GMT+01:00, Michael Paquier <michael.paquier@gmail.com> wrote:
, but we surely do not want to give away

checkpoint and recovery information.

Why is that? A lot of that information is available anyway?

We are trying to hide away from non-superusers WAL-related information
in system views and system function, that's my point to do the same
here. For the data of pg_control, it seems to me that this can give
away to any authorized users hints regarding the way Postgres is
built, perhaps letting people know for example which Linux
distribution is used and which flavor of Postgres is used (we already
give away some information with version() but that's different than
the libraries this is linking to), so an attacker may be able to take
advantage of that to do attacks on potentially outdated packages? And
I would think that many users are actually going to revoke the access
of those functions to public if we are going to make them
world-visible. It is easier as well to restrict things first, and then
relax if necessary, than the opposite as well.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#50Andres Freund
andres@anarazel.de
In reply to: Michael Paquier (#49)
Re: exposing pg_controldata and pg_config as functions

On 2016-01-18 10:18:34 +0900, Michael Paquier wrote:

We are trying to hide away from non-superusers WAL-related information
in system views and system function, that's my point to do the same
here.

We are? pg_current_xlog_insert_location(), pg_current_xlog_location(),
pg_is_xlog_replay_paused(), pg_stat_bgwriter ... are all non-superuser?

For the data of pg_control, it seems to me that this can give
away to any authorized users hints regarding the way Postgres is
built, perhaps letting people know for example which Linux
distribution is used and which flavor of Postgres is used (we already
give away some information with version() but that's different than
the libraries this is linking to), so an attacker may be able to take
advantage of that to do attacks on potentially outdated packages? And
I would think that many users are actually going to revoke the access
of those functions to public if we are going to make them
world-visible. It is easier as well to restrict things first, and then
relax if necessary, than the opposite as well.

Meh, that seems pretty far into pseudo security arguments.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Bruce Momjian
bruce@momjian.us
In reply to: Joe Conway (#46)
Re: exposing pg_controldata and pg_config as functions

On Sun, Jan 17, 2016 at 02:24:46PM -0800, Joe Conway wrote:

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

FYI, this slash-colon change will break pg_upgrade unless it is patched.
Dp you want a patch from me?

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52Joe Conway
mail@joeconway.com
In reply to: Bruce Momjian (#51)
Re: exposing pg_controldata and pg_config as functions

On 01/18/2016 01:47 PM, Bruce Momjian wrote:

On Sun, Jan 17, 2016 at 02:24:46PM -0800, Joe Conway wrote:

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

FYI, this slash-colon change will break pg_upgrade unless it is patched.
Dp you want a patch from me?

Didn't realize that -- yes please.

Thanks,

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#53Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#50)
Re: exposing pg_controldata and pg_config as functions

On Mon, Jan 18, 2016 at 4:43 AM, Andres Freund <andres@anarazel.de> wrote:

On 2016-01-18 10:18:34 +0900, Michael Paquier wrote:

We are trying to hide away from non-superusers WAL-related information
in system views and system function, that's my point to do the same
here.

We are? pg_current_xlog_insert_location(), pg_current_xlog_location(),
pg_is_xlog_replay_paused(), pg_stat_bgwriter ... are all non-superuser?

Yeah. There's certainly no need for the WAL positions reported by
pg_controldata to be any more restricted than other functions that
give away information about WAL position. We had some discussion
about restricting WAL position information in general due to possible
information leakage, and if we do that, then perhaps this should be
similarly restricted. Presumably vulnerabilities here would be harder
to exploit because the values change much less frequently, so if you
are trying to learn something the rate at which you can glean
information will be much lower. But maybe we should put the same
restrictions on all of it.

For the data of pg_control, it seems to me that this can give
away to any authorized users hints regarding the way Postgres is
built, perhaps letting people know for example which Linux
distribution is used and which flavor of Postgres is used (we already
give away some information with version() but that's different than
the libraries this is linking to), so an attacker may be able to take
advantage of that to do attacks on potentially outdated packages? And
I would think that many users are actually going to revoke the access
of those functions to public if we are going to make them
world-visible. It is easier as well to restrict things first, and then
relax if necessary, than the opposite as well.

Meh, that seems pretty far into pseudo security arguments.

Yeah, I really don't see anything in the pg_controldata output that
looks sensitive. The WAL locations are the closest of anything,
AFAICS.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Stephen Frost
sfrost@snowman.net
In reply to: Robert Haas (#53)
Re: exposing pg_controldata and pg_config as functions

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 18, 2016 at 4:43 AM, Andres Freund <andres@anarazel.de> wrote:

Meh, that seems pretty far into pseudo security arguments.

Yeah, I really don't see anything in the pg_controldata output that
looks sensitive. The WAL locations are the closest of anything,
AFAICS.

Heikki already showed how the WAL location information could be
exploited if compression is enabled.

I believe that's something we should care about and fix in one way or
another (my initial approach was using defualt roles, but using the ACL
system and starting out w/ no rights granted to that function also
works).

Thanks!

Stephen

#55Andres Freund
andres@anarazel.de
In reply to: Stephen Frost (#54)
Re: exposing pg_controldata and pg_config as functions

On January 18, 2016 11:10:35 PM GMT+01:00, Stephen Frost <sfrost@snowman.net> wrote:

* Robert Haas (robertmhaas@gmail.com) wrote:

On Mon, Jan 18, 2016 at 4:43 AM, Andres Freund <andres@anarazel.de>

wrote:

Meh, that seems pretty far into pseudo security arguments.

Yeah, I really don't see anything in the pg_controldata output that
looks sensitive. The WAL locations are the closest of anything,
AFAICS.

Heikki already showed how the WAL location information could be
exploited if compression is enabled.

I believe that's something we should care about and fix in one way or
another (my initial approach was using defualt roles, but using the ACL
system and starting out w/ no rights granted to that function also
works).

Sure. But it's pointless to make things more complicated when there's functions providing equivalent information already.

--- 
Please excuse brevity and formatting - I am writing this on my mobile phone.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Joe Conway
mail@joeconway.com
In reply to: Joe Conway (#47)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 01/17/2016 02:29 PM, Joe Conway wrote:

Documentation would be good to have.

I'm definitely happy to write the docs, but earlier it was not clear
that there was enough support for this patch at all, and I don't want to
waste cycles writing docs for a feature that ultimately does not get
committed. What's the current feel for whether this feature in general
is a good idea or bad?

Thoughts anyone?

! # 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)\""
This duplication from src/bin/pg_config is a bad idea. Couldn't we do
something in src/common instead that sets up values at compilation
time in a routine (perhaps set of routines) available for both the
frontend and backend?

Will take a look at it.

Please see the attached. Duplication removed.

Thanks,

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_config-2016.01.18.01.difftext/x-diff; name=pg_config-2016.01.18.01.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..abf9a70 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..a0c82c1 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
index ...05ee67b .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,102 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Portions Copyright (c) 1996-2015, 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/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b14294..591402e 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 25,387 ****
  #include "postgres_fe.h"
  
  #include "port.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
--- 25,33 ----
  #include "postgres_fe.h"
  
  #include "port.h"
+ #include "common/config_info.h"
  
  static const char *progname;
  
  /*
   * Table of known information items
*************** show_version(bool all)
*** 391,423 ****
  typedef struct
  {
  	const char *switchname;
! 	void		(*show_func) (bool all);
  } 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},
  	{NULL, NULL}
  };
  
--- 37,69 ----
  typedef struct
  {
  	const char *switchname;
! 	const char *configname;
  } InfoItem;
  
  static const InfoItem info_items[] = {
! 	{"--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}
  };
  
*************** advice(void)
*** 466,487 ****
  }
  
  static void
! show_all(void)
  {
  	int			i;
  
! 	for (i = 0; info_items[i].switchname != NULL; i++)
  	{
! 		(*info_items[i].show_func) (true);
  	}
  }
  
  int
  main(int argc, char **argv)
  {
  	int			i;
  	int			j;
- 	int			ret;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
--- 112,139 ----
  }
  
  static void
! show_item(const char *configname,
! 		  configdata *ConfigData,
! 		  size_t configdata_len)
  {
  	int			i;
  
! 	for (i = 0; i < configdata_len; i++)
  	{
! 		if (strcmp(configname, ConfigData[i].name) == 0)
! 			printf("%s = %s\n", ConfigData[i].name, ConfigData[i].setting);
! 		++i;
  	}
  }
  
  int
  main(int argc, char **argv)
  {
+ 	configdata *ConfigData;
+ 	size_t		configdata_len;
+ 	char		my_exec_path[MAXPGPATH];
  	int			i;
  	int			j;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
*************** main(int argc, char **argv)
*** 497,524 ****
  		}
  	}
  
! 	ret = find_my_exec(argv[0], mypath);
! 
! 	if (ret)
  	{
  		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
  		exit(1);
  	}
  
  	/* no arguments -> print everything */
  	if (argc < 2)
  	{
! 		show_all();
  		exit(0);
  	}
  
  	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);
  				break;
  			}
  		}
--- 149,178 ----
  		}
  	}
  
! 	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)
  	{
! 		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)
  			{
! 				show_item(info_items[j].configname,
! 						  ConfigData, configdata_len);
  				break;
  			}
  		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..0cbee41 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,43 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! # 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 = exec.o config_info.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
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 3df5ac5..ffc8096 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5186,5191 ****
--- 5186,5194 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* 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
diff --git a/src/include/port.h b/src/include/port.h
index 9fc79f4..cb13dd8 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 477fde1..aa713c1 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1126,1131 ****
--- 1126,1134 ----
  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 a418f93..5c9de0c 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 28b061f..4c84411 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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,
#57Michael Paquier
michael.paquier@gmail.com
In reply to: Robert Haas (#53)
Re: exposing pg_controldata and pg_config as functions

On Tue, Jan 19, 2016 at 6:55 AM, Robert Haas <robertmhaas@gmail.com> wrote:

On Mon, Jan 18, 2016 at 4:43 AM, Andres Freund <andres@anarazel.de> wrote:

On 2016-01-18 10:18:34 +0900, Michael Paquier wrote:

We are trying to hide away from non-superusers WAL-related information
in system views and system function, that's my point to do the same
here.

We are? pg_current_xlog_insert_location(), pg_current_xlog_location(),
pg_is_xlog_replay_paused(), pg_stat_bgwriter ... are all non-superuser?

Yeah. There's certainly no need for the WAL positions reported by
pg_controldata to be any more restricted than other functions that
give away information about WAL position. We had some discussion
about restricting WAL position information in general due to possible
information leakage, and if we do that, then perhaps this should be
similarly restricted. Presumably vulnerabilities here would be harder
to exploit because the values change much less frequently, so if you
are trying to learn something the rate at which you can glean
information will be much lower. But maybe we should put the same
restrictions on all of it.

Well, we can still use REVOKE on those functions, so it is not like a
user cannot restrict the access to this information. The current
situation makes it hard for both us and the user to figure out if an
instance is considered as secure or not, so things are unbalanced.
Perhaps the best answer is to add a documentation section to tell
people how to harden their database after initdb'ing it, with
different sections aimed at hardening different things, one being the
WAL information, and mention as well in those docs which hardening
action covers what. Stephen mentioned that some time ago, that would
still be good.

For the data of pg_control, it seems to me that this can give
away to any authorized users hints regarding the way Postgres is
built, perhaps letting people know for example which Linux
distribution is used and which flavor of Postgres is used (we already
give away some information with version() but that's different than
the libraries this is linking to), so an attacker may be able to take
advantage of that to do attacks on potentially outdated packages? And
I would think that many users are actually going to revoke the access
of those functions to public if we are going to make them
world-visible. It is easier as well to restrict things first, and then
relax if necessary, than the opposite as well.

Meh, that seems pretty far into pseudo security arguments.

Yeah, I really don't see anything in the pg_controldata output that
looks sensitive. The WAL locations are the closest of anything,
AFAICS.

The system identifier perhaps? I honestly don't have on top of my head
a way to exploit this information but leaking that at SQL level seems
sensible: that's a unique identifier of a Postgres instance used when
setting up a cluster after all.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58Bruce Momjian
bruce@momjian.us
In reply to: Joe Conway (#52)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

fOn Mon, Jan 18, 2016 at 01:54:02PM -0800, Joe Conway wrote:

On 01/18/2016 01:47 PM, Bruce Momjian wrote:

On Sun, Jan 17, 2016 at 02:24:46PM -0800, Joe Conway wrote:

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

FYI, this slash-colon change will break pg_upgrade unless it is patched.
Dp you want a patch from me?

Didn't realize that -- yes please.

Sure, attached, and it would be applied only to head, where you change
pg_controldata. pg_upgrade has to read the old and new cluster's
pg_controldata. We could get more sophisticated by checking the catalog
version number where the format was changed, but that doesn't seem worth
it, and is overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

I can test all suppored Postgres versions with pg_upgrade once you apply
the patch, but I think it will be fine.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

Attachments:

pg_upgrade.difftext/x-diff; charset=us-asciiDownload
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
new file mode 100644
index 1f7b65e..aaaea7b
*** a/src/bin/pg_upgrade/controldata.c
--- b/src/bin/pg_upgrade/controldata.c
*************** get_control_data(ClusterInfo *cluster, b
*** 198,203 ****
--- 198,206 ----
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
  			p = strchr(p, '/');
+ 			/* delimiter changed from '/' to ':' in 9.6 */
+ 			if (p == NULL && GET_MAJOR_VERSION(cluster->major_version) >= 906)
+ 				p = strchr(p, ':');
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
#59Joe Conway
mail@joeconway.com
In reply to: Joe Conway (#56)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 01/18/2016 04:16 PM, Joe Conway wrote:

Please see the attached. Duplication removed.

Actually please see this version instead.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_config-2016.01.18.02.difftext/x-diff; name=pg_config-2016.01.18.02.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..abf9a70 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..a0c82c1 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
index ...05ee67b .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,102 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Portions Copyright (c) 1996-2015, 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/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b14294..963a244 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 25,387 ****
  #include "postgres_fe.h"
  
  #include "port.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
--- 25,33 ----
  #include "postgres_fe.h"
  
  #include "port.h"
+ #include "common/config_info.h"
  
  static const char *progname;
  
  /*
   * Table of known information items
*************** show_version(bool all)
*** 391,423 ****
  typedef struct
  {
  	const char *switchname;
! 	void		(*show_func) (bool all);
  } 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},
  	{NULL, NULL}
  };
  
--- 37,69 ----
  typedef struct
  {
  	const char *switchname;
! 	const char *configname;
  } InfoItem;
  
  static const InfoItem info_items[] = {
! 	{"--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}
  };
  
*************** advice(void)
*** 466,487 ****
  }
  
  static void
! show_all(void)
  {
  	int			i;
  
! 	for (i = 0; info_items[i].switchname != NULL; i++)
  	{
! 		(*info_items[i].show_func) (true);
  	}
  }
  
  int
  main(int argc, char **argv)
  {
  	int			i;
  	int			j;
- 	int			ret;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
--- 112,138 ----
  }
  
  static void
! show_item(const char *configname,
! 		  configdata *ConfigData,
! 		  size_t configdata_len)
  {
  	int			i;
  
! 	for (i = 0; i < configdata_len; i++)
  	{
! 		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;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
*************** main(int argc, char **argv)
*** 497,524 ****
  		}
  	}
  
! 	ret = find_my_exec(argv[0], mypath);
! 
! 	if (ret)
  	{
  		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
  		exit(1);
  	}
  
  	/* no arguments -> print everything */
  	if (argc < 2)
  	{
! 		show_all();
  		exit(0);
  	}
  
  	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);
  				break;
  			}
  		}
--- 148,177 ----
  		}
  	}
  
! 	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)
  	{
! 		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)
  			{
! 				show_item(info_items[j].configname,
! 						  ConfigData, configdata_len);
  				break;
  			}
  		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..0cbee41 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,43 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! # 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 = exec.o config_info.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
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 3df5ac5..ffc8096 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5186,5191 ****
--- 5186,5194 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* 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
diff --git a/src/include/port.h b/src/include/port.h
index 9fc79f4..cb13dd8 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 477fde1..aa713c1 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1126,1131 ****
--- 1126,1134 ----
  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 a418f93..5c9de0c 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 28b061f..4c84411 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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,
#60Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#59)
Re: exposing pg_controldata and pg_config as functions

On Tue, Jan 19, 2016 at 11:08 AM, Joe Conway <mail@joeconway.com> wrote:

On 01/18/2016 04:16 PM, Joe Conway wrote:

Please see the attached. Duplication removed.

Actually please see this version instead.

Thanks for the new patch.

+               tuplestore_puttuple(tupstore, tuple);
+       }
+
+       /*
+        * no longer need the tuple descriptor reference created by
The patch has some whitespaces.
+REVOKE ALL on pg_config FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
I guess that this portion is still under debate :)

make[1]: Nothing to be done for `all'.
make -C ../backend submake-errcodes
make[2]: *** No rule to make target `config_info.o', needed by
`libpgcommon.a'. Stop.
make[2]: *** Waiting for unfinished jobs....
The patch is visibly forgetting to include config_info.c, which should
be part of src/common.

 /*
+ * 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)
+{
Perhaps this refactoring would be useful as a separate patch?
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#60)
Re: exposing pg_controldata and pg_config as functions

On Tue, Jan 19, 2016 at 1:49 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Jan 19, 2016 at 11:08 AM, Joe Conway <mail@joeconway.com> wrote:

On 01/18/2016 04:16 PM, Joe Conway wrote:

Please see the attached. Duplication removed.

Actually please see this version instead.

Thanks for the new patch.

+               tuplestore_puttuple(tupstore, tuple);
+       }
+
+       /*
+        * no longer need the tuple descriptor reference created by
The patch has some whitespaces.
+REVOKE ALL on pg_config FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
I guess that this portion is still under debate :)

make[1]: Nothing to be done for `all'.
make -C ../backend submake-errcodes
make[2]: *** No rule to make target `config_info.o', needed by
`libpgcommon.a'. Stop.
make[2]: *** Waiting for unfinished jobs....
The patch is visibly forgetting to include config_info.c, which should
be part of src/common.

/*
+ * 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)
+{
Perhaps this refactoring would be useful as a separate patch?

You need as well to update @pgcommonallfiles in Mkvcbuild.pm or the
compilation with MSVC is going to fail.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#62Bruce Momjian
bruce@momjian.us
In reply to: Bruce Momjian (#58)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On Mon, Jan 18, 2016 at 07:50:12PM -0500, Bruce Momjian wrote:

1) Change NextXID output format from "%u/%u" to "%u:%u"
(see recent hackers thread)

!     printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
!     printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
This should be definitely a separate patch.

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

FYI, this slash-colon change will break pg_upgrade unless it is patched.
Dp you want a patch from me?

Didn't realize that -- yes please.

Sure, attached, and it would be applied only to head, where you change
pg_controldata. pg_upgrade has to read the old and new cluster's
pg_controldata. We could get more sophisticated by checking the catalog
version number where the format was changed, but that doesn't seem worth
it, and is overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

Sorry, please use the attached patch instead, now tested with your
changes.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

Attachments:

pg_upgrade.difftext/x-diff; charset=us-asciiDownload
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
new file mode 100644
index 2def729..34e194c
*** a/src/bin/pg_upgrade/controldata.c
--- b/src/bin/pg_upgrade/controldata.c
*************** get_control_data(ClusterInfo *cluster, b
*** 197,207 ****
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			p = strchr(p, '/');
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
--- 197,214 ----
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			if (strchr(p, '/') != NULL)
! 				p = strchr(p, '/');
! 			/* delimiter changed from '/' to ':' in 9.6 */
! 			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
! 				p = strchr(p, ':');
! 			else
! 				p = NULL;
! 
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' or ':' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
#63Robert Haas
robertmhaas@gmail.com
In reply to: Michael Paquier (#57)
Re: exposing pg_controldata and pg_config as functions

On Mon, Jan 18, 2016 at 7:42 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

Yeah, I really don't see anything in the pg_controldata output that
looks sensitive. The WAL locations are the closest of anything,
AFAICS.

The system identifier perhaps? I honestly don't have on top of my head
a way to exploit this information but leaking that at SQL level seems
sensible: that's a unique identifier of a Postgres instance used when
setting up a cluster after all.

I think you are confusing useful information with security-sensitive
information. The system identifier may be useful, but if you can't
use it to compromise something, it's not security-sensitive.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#64Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#61)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 01/18/2016 08:51 PM, Michael Paquier wrote:

On Tue, Jan 19, 2016 at 1:49 PM, Michael Paquier

+       }
+
+       /*
+        * no longer need the tuple descriptor reference created by
The patch has some whitespaces.

I take it you mean a line with only whitespace? Fixed.

+REVOKE ALL on pg_config FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
I guess that this portion is still under debate :)

Left in for the moment.

make[1]: Nothing to be done for `all'.
make -C ../backend submake-errcodes
make[2]: *** No rule to make target `config_info.o', needed by
`libpgcommon.a'. Stop.
make[2]: *** Waiting for unfinished jobs....
The patch is visibly forgetting to include config_info.c, which should
be part of src/common.

Confounded git! ;-)
Fixed.

/*
+ * 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)
+{
Perhaps this refactoring would be useful as a separate patch?

It doesn't seem worth doing on its own, and it is required by the rest
of this patch. I'd say keep it here, but I'll break it out if you think
it important enough.

You need as well to update @pgcommonallfiles in Mkvcbuild.pm or the
compilation with MSVC is going to fail.

Good catch -- done.

The only things I know of still lacking is:
1) Documentation
2) Decision on REVOKE ... FROM PUBLIC

I'm assuming by the lack of complainants that there is enough support
for the feature (conceptually at least) that it is worthwhile for me to
write the docs. Will do that over the next couple of days or so.

Thanks for the code reviews!

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_config-2016.01.19.00.difftext/x-diff; name=pg_config-2016.01.19.00.diffDownload
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..abf9a70 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..a0c82c1 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
index ...d86460e .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,102 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Portions Copyright (c) 1996-2015, 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/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b14294..963a244 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 25,387 ****
  #include "postgres_fe.h"
  
  #include "port.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
--- 25,33 ----
  #include "postgres_fe.h"
  
  #include "port.h"
+ #include "common/config_info.h"
  
  static const char *progname;
  
  /*
   * Table of known information items
*************** show_version(bool all)
*** 391,423 ****
  typedef struct
  {
  	const char *switchname;
! 	void		(*show_func) (bool all);
  } 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},
  	{NULL, NULL}
  };
  
--- 37,69 ----
  typedef struct
  {
  	const char *switchname;
! 	const char *configname;
  } InfoItem;
  
  static const InfoItem info_items[] = {
! 	{"--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}
  };
  
*************** advice(void)
*** 466,487 ****
  }
  
  static void
! show_all(void)
  {
  	int			i;
  
! 	for (i = 0; info_items[i].switchname != NULL; i++)
  	{
! 		(*info_items[i].show_func) (true);
  	}
  }
  
  int
  main(int argc, char **argv)
  {
  	int			i;
  	int			j;
- 	int			ret;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
--- 112,138 ----
  }
  
  static void
! show_item(const char *configname,
! 		  configdata *ConfigData,
! 		  size_t configdata_len)
  {
  	int			i;
  
! 	for (i = 0; i < configdata_len; i++)
  	{
! 		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;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
*************** main(int argc, char **argv)
*** 497,524 ****
  		}
  	}
  
! 	ret = find_my_exec(argv[0], mypath);
! 
! 	if (ret)
  	{
  		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
  		exit(1);
  	}
  
  	/* no arguments -> print everything */
  	if (argc < 2)
  	{
! 		show_all();
  		exit(0);
  	}
  
  	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);
  				break;
  			}
  		}
--- 148,177 ----
  		}
  	}
  
! 	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)
  	{
! 		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)
  			{
! 				show_item(info_items[j].configname,
! 						  ConfigData, configdata_len);
  				break;
  			}
  		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..0cbee41 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,43 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! # 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 = exec.o config_info.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
  
diff --git a/src/common/config_info.c b/src/common/config_info.c
index ...96f0e7b .
*** a/src/common/config_info.c
--- b/src/common/config_info.c
***************
*** 0 ****
--- 1,196 ----
+ /*-------------------------------------------------------------------------
+  *
+  * config_info.c
+  *		Common code for pg_config output
+  *
+  *
+  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "miscadmin.h"
+ #include "common/config_info.h"
+ 
+ static size_t ConfigDataNames_len(void);
+ 
+ static const char *const ConfigDataNames[] =
+ {
+ 	"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
+ ConfigDataNames_len(void)
+ {
+ 	size_t	i = 0;
+ 
+ 	while (ConfigDataNames[i])
+ 		i++;
+ 
+ 	return i;
+ }
+ 
+ configdata *
+ get_configdata(char *my_exec_path, size_t *configdata_len)
+ {
+ 	configdata	   *ConfigData;
+ 	char			path[MAXPGPATH];
+ 	char		   *lastsep;
+ 	int				i;
+ 
+ 	*configdata_len = ConfigDataNames_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(ConfigDataNames[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/pg_proc.h b/src/include/catalog/pg_proc.h
index 3df5ac5..ffc8096 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5186,5191 ****
--- 5186,5194 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* 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
diff --git a/src/include/common/config_info.h b/src/include/common/config_info.h
index ...762b66f .
*** a/src/include/common/config_info.h
--- b/src/include/common/config_info.h
***************
*** 0 ****
--- 1,21 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  *	src/include/common/controldata_utils.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 9fc79f4..cb13dd8 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 477fde1..aa713c1 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1126,1131 ****
--- 1126,1134 ----
  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 a418f93..5c9de0c 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 28b061f..4c84411 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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 1dba7d9..cc0dcb3 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,113 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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
--- 106,113 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  exec.c config_info.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
#65Joe Conway
mail@joeconway.com
In reply to: Bruce Momjian (#62)
1 attachment(s)
NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On 01/19/2016 09:02 AM, Bruce Momjian wrote:

Ok. Notwithstanding Simon's reply, there seems to be consensus that this
is the way to go. Will commit it this way unless some additional
objections surface in the next day or so.

FYI, this slash-colon change will break pg_upgrade unless it is patched.
Dp you want a patch from me?

Didn't realize that -- yes please.

Sure, attached, and it would be applied only to head, where you change
pg_controldata. pg_upgrade has to read the old and new cluster's
pg_controldata. We could get more sophisticated by checking the catalog
version number where the format was changed, but that doesn't seem worth
it, and is overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

Sorry, please use the attached patch instead, now tested with your
changes.

The attached includes Bruce's change, plus I found two additional sites
that appear to need the same change. The xlog.c change is just a DEBUG
message, so not a big deal. I'm less certain if the xlogdesc.c change
might create some fallout.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

NextXID-delimiter-chg-2016.01.19.00.difftext/x-diff; name=NextXID-delimiter-chg-2016.01.19.00.diffDownload
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index b694dea..2dcbfbd 100644
*** a/src/backend/access/rmgrdesc/xlogdesc.c
--- b/src/backend/access/rmgrdesc/xlogdesc.c
*************** xlog_desc(StringInfo buf, XLogReaderStat
*** 43,49 ****
  		CheckPoint *checkpoint = (CheckPoint *) rec;
  
  		appendStringInfo(buf, "redo %X/%X; "
! 						 "tli %u; prev tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
  						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
  						 "oldest/newest commit timestamp xid: %u/%u; "
  						 "oldest running xid %u; %s",
--- 43,49 ----
  		CheckPoint *checkpoint = (CheckPoint *) rec;
  
  		appendStringInfo(buf, "redo %X/%X; "
! 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
  						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
  						 "oldest/newest commit timestamp xid: %u/%u; "
  						 "oldest running xid %u; %s",
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7d5d493..ee87e1b 100644
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** StartupXLOG(void)
*** 6284,6290 ****
  				  (uint32) (checkPoint.redo >> 32), (uint32) checkPoint.redo,
  					wasShutdown ? "TRUE" : "FALSE")));
  	ereport(DEBUG1,
! 			(errmsg_internal("next transaction ID: %u/%u; next OID: %u",
  					checkPoint.nextXidEpoch, checkPoint.nextXid,
  					checkPoint.nextOid)));
  	ereport(DEBUG1,
--- 6284,6290 ----
  				  (uint32) (checkPoint.redo >> 32), (uint32) checkPoint.redo,
  					wasShutdown ? "TRUE" : "FALSE")));
  	ereport(DEBUG1,
! 			(errmsg_internal("next transaction ID: %u:%u; next OID: %u",
  					checkPoint.nextXidEpoch, checkPoint.nextXid,
  					checkPoint.nextOid)));
  	ereport(DEBUG1,
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index e7e072f..5dd2dbc 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
*************** main(int argc, char *argv[])
*** 252,258 ****
  		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 252,258 ----
  		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index ca706a5..525b82b 100644
*** a/src/bin/pg_resetxlog/pg_resetxlog.c
--- b/src/bin/pg_resetxlog/pg_resetxlog.c
*************** PrintControlValues(bool guessed)
*** 646,652 ****
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 2def729..34e194c 100644
*** a/src/bin/pg_upgrade/controldata.c
--- b/src/bin/pg_upgrade/controldata.c
*************** get_control_data(ClusterInfo *cluster, b
*** 197,207 ****
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			p = strchr(p, '/');
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
--- 197,214 ----
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			if (strchr(p, '/') != NULL)
! 				p = strchr(p, '/');
! 			/* delimiter changed from '/' to ':' in 9.6 */
! 			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
! 				p = strchr(p, ':');
! 			else
! 				p = NULL;
! 
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' or ':' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
#66Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Joe Conway (#65)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

Joe Conway wrote:

The attached includes Bruce's change, plus I found two additional sites
that appear to need the same change. The xlog.c change is just a DEBUG
message, so not a big deal. I'm less certain if the xlogdesc.c change
might create some fallout.

Hm, pg_xlogdump links the rmgrdesc files, so perhaps you might need to
adjust expected test output for it. Not really sure.

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#67Michael Paquier
michael.paquier@gmail.com
In reply to: Alvaro Herrera (#66)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Wed, Jan 20, 2016 at 11:41 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Joe Conway wrote:

The attached includes Bruce's change, plus I found two additional sites
that appear to need the same change. The xlog.c change is just a DEBUG
message, so not a big deal. I'm less certain if the xlogdesc.c change
might create some fallout.

Hm, pg_xlogdump links the rmgrdesc files, so perhaps you might need to
adjust expected test output for it. Not really sure.

We don't depend on this output format in any tests AFAIK, at least
check-world is not complaining here and pg_xlogdump has no dedicated
tests. There may be some utility in the outside world doing some
manipulation of the string generated for this record, but that's not
worth worrying about anyway.

Patch looks fine, I have not spotted any other places that need a refresh.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#68Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#64)
Re: exposing pg_controldata and pg_config as functions

On Wed, Jan 20, 2016 at 2:32 AM, Joe Conway <mail@joeconway.com> wrote:

The only things I know of still lacking is:
1) Documentation
2) Decision on REVOKE ... FROM PUBLIC

Yep, regarding 2) I am the only one actually making noise to protect
this information by default, against at least 2 committers :)

I'm assuming by the lack of complainants that there is enough support
for the feature (conceptually at least) that it is worthwhile for me to
write the docs. Will do that over the next couple of days or so.

I would think so as well.

+typedef struct configdata
+{
+       char       *name;
+       char       *setting;
+} configdata;
For a better analogy to ControlFileData, this could be renamed ConfigData?
-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))
The point of the move to src/common is to remove the duplication in
src/bin/pg_config/Makefile.
--- /dev/null
+++ b/src/common/config_info.c
[...]
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "common/config_info.h"
All the files in src/common should begin their include declarations with that:
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
+configdata *
+get_configdata(char *my_exec_path, size_t *configdata_len)
+{
It may be good to mention that the result is palloc'd and that caller
may need to free it if necessary. It does not matter in the two code
paths of this patch, but it may matter for other users calling that.

MSVC compilation is working correctly here.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#69Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#68)
Re: exposing pg_controldata and pg_config as functions

On Thu, Jan 21, 2016 at 1:08 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Wed, Jan 20, 2016 at 2:32 AM, Joe Conway <mail@joeconway.com> wrote:

The only things I know of still lacking is:
1) Documentation
2) Decision on REVOKE ... FROM PUBLIC

Yep, regarding 2) I am the only one actually making noise to protect
this information by default, against at least 2 committers :)

I'm assuming by the lack of complainants that there is enough support
for the feature (conceptually at least) that it is worthwhile for me to
write the docs. Will do that over the next couple of days or so.

I would think so as well.

+typedef struct configdata
+{
+       char       *name;
+       char       *setting;
+} configdata;
For a better analogy to ControlFileData, this could be renamed ConfigData?
-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))
The point of the move to src/common is to remove the duplication in
src/bin/pg_config/Makefile.
--- /dev/null
+++ b/src/common/config_info.c
[...]
+#include "postgres.h"
+
+#include "miscadmin.h"
+#include "common/config_info.h"
All the files in src/common should begin their include declarations with that:
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
+configdata *
+get_configdata(char *my_exec_path, size_t *configdata_len)
+{
It may be good to mention that the result is palloc'd and that caller
may need to free it if necessary. It does not matter in the two code
paths of this patch, but it may matter for other users calling that.

MSVC compilation is working correctly here.

I am marking this patch as returned with feedback for now, not all the
issues have been fixed yet, and there are still no docs (the
conclusion being that people would like to have this stuff, right?).
Feel free to move it to the next CF should a new version be written.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#70Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#67)
1 attachment(s)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On 01/19/2016 07:04 PM, Michael Paquier wrote:

On Wed, Jan 20, 2016 at 11:41 AM, Alvaro Herrera
<alvherre@2ndquadrant.com> wrote:

Joe Conway wrote:

The attached includes Bruce's change, plus I found two additional sites
that appear to need the same change. The xlog.c change is just a DEBUG
message, so not a big deal. I'm less certain if the xlogdesc.c change
might create some fallout.

Hm, pg_xlogdump links the rmgrdesc files, so perhaps you might need to
adjust expected test output for it. Not really sure.

We don't depend on this output format in any tests AFAIK, at least
check-world is not complaining here and pg_xlogdump has no dedicated
tests. There may be some utility in the outside world doing some
manipulation of the string generated for this record, but that's not
worth worrying about anyway.

Patch looks fine, I have not spotted any other places that need a refresh.

I'll commit the attached tomorrow if there are no other concerns voiced.

In the spirit of the dev meeting discussion, I am trying to use the
commit message template discussed. Something like:

-- email subject limit -----------------------------------------
Change delimiter used for display of NextXID

NextXID has been rendered in the form of a pg_lsn even though it
really is not. This can cause confusion, so change the format from
%u/%u to %u:%u, per discussion on hackers.

Complaint by me, patch by me and Bruce, reviewed by Michael Paquier
and Alvaro. Applied to HEAD only.

Reported-by: Joe Conway
Author: Joe Conway, Bruce Momjian
Reviewed-by: Michael Paquier, Alvaro Herrera
Tested-by: Michael Paquier
Backpatch-through: master
-- email subject limit -----------------------------------------

That does look pretty redundant though. Thoughts?

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

NextXID-delimiter-chg-2016.01.19.00.difftext/x-diff; name=NextXID-delimiter-chg-2016.01.19.00.diffDownload
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index b694dea..2dcbfbd 100644
*** a/src/backend/access/rmgrdesc/xlogdesc.c
--- b/src/backend/access/rmgrdesc/xlogdesc.c
*************** xlog_desc(StringInfo buf, XLogReaderStat
*** 43,49 ****
  		CheckPoint *checkpoint = (CheckPoint *) rec;
  
  		appendStringInfo(buf, "redo %X/%X; "
! 						 "tli %u; prev tli %u; fpw %s; xid %u/%u; oid %u; multi %u; offset %u; "
  						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
  						 "oldest/newest commit timestamp xid: %u/%u; "
  						 "oldest running xid %u; %s",
--- 43,49 ----
  		CheckPoint *checkpoint = (CheckPoint *) rec;
  
  		appendStringInfo(buf, "redo %X/%X; "
! 						 "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
  						 "oldest xid %u in DB %u; oldest multi %u in DB %u; "
  						 "oldest/newest commit timestamp xid: %u/%u; "
  						 "oldest running xid %u; %s",
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7d5d493..ee87e1b 100644
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
*************** StartupXLOG(void)
*** 6284,6290 ****
  				  (uint32) (checkPoint.redo >> 32), (uint32) checkPoint.redo,
  					wasShutdown ? "TRUE" : "FALSE")));
  	ereport(DEBUG1,
! 			(errmsg_internal("next transaction ID: %u/%u; next OID: %u",
  					checkPoint.nextXidEpoch, checkPoint.nextXid,
  					checkPoint.nextOid)));
  	ereport(DEBUG1,
--- 6284,6290 ----
  				  (uint32) (checkPoint.redo >> 32), (uint32) checkPoint.redo,
  					wasShutdown ? "TRUE" : "FALSE")));
  	ereport(DEBUG1,
! 			(errmsg_internal("next transaction ID: %u:%u; next OID: %u",
  					checkPoint.nextXidEpoch, checkPoint.nextXid,
  					checkPoint.nextOid)));
  	ereport(DEBUG1,
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index e7e072f..5dd2dbc 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
*************** main(int argc, char *argv[])
*** 252,258 ****
  		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 252,258 ----
  		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index ca706a5..525b82b 100644
*** a/src/bin/pg_resetxlog/pg_resetxlog.c
--- b/src/bin/pg_resetxlog/pg_resetxlog.c
*************** PrintControlValues(bool guessed)
*** 646,652 ****
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u/%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
--- 646,652 ----
  		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
  		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
! 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
  		   ControlFile.checkPointCopy.nextXidEpoch,
  		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 2def729..34e194c 100644
*** a/src/bin/pg_upgrade/controldata.c
--- b/src/bin/pg_upgrade/controldata.c
*************** get_control_data(ClusterInfo *cluster, b
*** 197,207 ****
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			p = strchr(p, '/');
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
--- 197,214 ----
  			p++;				/* remove ':' char */
  			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
  
! 			if (strchr(p, '/') != NULL)
! 				p = strchr(p, '/');
! 			/* delimiter changed from '/' to ':' in 9.6 */
! 			else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
! 				p = strchr(p, ':');
! 			else
! 				p = NULL;
! 
  			if (p == NULL || strlen(p) <= 1)
  				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
  
! 			p++;				/* remove '/' or ':' char */
  			cluster->controldata.chkpnt_nxtxid = str2uint(p);
  			got_xid = true;
  		}
#71Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#70)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Wed, Feb 10, 2016 at 9:57 AM, Joe Conway <mail@joeconway.com> wrote:

I'll commit the attached tomorrow if there are no other concerns voiced.

Just a nitpick regarding this block:
+           if (strchr(p, '/') != NULL)
+               p = strchr(p, '/');
+           /* delimiter changed from '/' to ':' in 9.6 */
+           else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
+               p = strchr(p, ':');
+           else
+               p = NULL;
Changing it as follows would save some instructions because there is
no need to call strchr an extra time:
if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
    p = strchr(p, ':');
else
    p = strchr(p, '/');

In the spirit of the dev meeting discussion, I am trying to use the
commit message template discussed. Something like:

-- email subject limit -----------------------------------------
Change delimiter used for display of NextXID

NextXID has been rendered in the form of a pg_lsn even though it
really is not. This can cause confusion, so change the format from
%u/%u to %u:%u, per discussion on hackers.

Complaint by me, patch by me and Bruce, reviewed by Michael Paquier
and Alvaro. Applied to HEAD only.

Reported-by: Joe Conway
Author: Joe Conway, Bruce Momjian
Reviewed-by: Michael Paquier, Alvaro Herrera
Tested-by: Michael Paquier
Backpatch-through: master
That does look pretty redundant though. Thoughts?

Removing Tested-by would be fine here I guess, testing and review are
overlapping concepts for this patch.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#72Bruce Momjian
bruce@momjian.us
In reply to: Michael Paquier (#71)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Wed, Feb 10, 2016 at 10:23:41AM +0900, Michael Paquier wrote:

On Wed, Feb 10, 2016 at 9:57 AM, Joe Conway <mail@joeconway.com> wrote:

I'll commit the attached tomorrow if there are no other concerns voiced.

Just a nitpick regarding this block:
+           if (strchr(p, '/') != NULL)
+               p = strchr(p, '/');
+           /* delimiter changed from '/' to ':' in 9.6 */
+           else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
+               p = strchr(p, ':');
+           else
+               p = NULL;
Changing it as follows would save some instructions because there is
no need to call strchr an extra time:
if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
p = strchr(p, ':');
else
p = strchr(p, '/');

No, that is not an improvement --- see my previous comment:

We could get more sophisticated by checking the catalog version number
where the format was changed, but that doesn't seem worth it, and is
overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

By testing for '906', you prevent users from using pg_upgrade to go from
one catalog version of 9.6 to a later one. Few people may want to do
it, but it should work.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#73Michael Paquier
michael.paquier@gmail.com
In reply to: Bruce Momjian (#72)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Fri, Feb 12, 2016 at 9:18 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Wed, Feb 10, 2016 at 10:23:41AM +0900, Michael Paquier wrote:

On Wed, Feb 10, 2016 at 9:57 AM, Joe Conway <mail@joeconway.com> wrote:

I'll commit the attached tomorrow if there are no other concerns voiced.

Just a nitpick regarding this block:
+           if (strchr(p, '/') != NULL)
+               p = strchr(p, '/');
+           /* delimiter changed from '/' to ':' in 9.6 */
+           else if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
+               p = strchr(p, ':');
+           else
+               p = NULL;
Changing it as follows would save some instructions because there is
no need to call strchr an extra time:
if (GET_MAJOR_VERSION(cluster->major_version) >= 906)
p = strchr(p, ':');
else
p = strchr(p, '/');

No, that is not an improvement --- see my previous comment:

We could get more sophisticated by checking the catalog version number
where the format was changed, but that doesn't seem worth it, and is
overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

By testing for '906', you prevent users from using pg_upgrade to go from
one catalog version of 9.6 to a later one. Few people may want to do
it, but it should work.

OK, I see now. I did not consider the case where people would like to
get upgrade from a dev version of 9.6 to the latest 9.6 version, just
the upgrade from a previous major version <= 9.5. Thanks for reminding
that pg_upgrade needs to support that.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#74Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#73)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On 02/11/2016 04:59 PM, Michael Paquier wrote:

On Fri, Feb 12, 2016 at 9:18 AM, Bruce Momjian <bruce@momjian.us> wrote:

No, that is not an improvement --- see my previous comment:

We could get more sophisticated by checking the catalog version number
where the format was changed, but that doesn't seem worth it, and is
overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

By testing for '906', you prevent users from using pg_upgrade to go from
one catalog version of 9.6 to a later one. Few people may want to do
it, but it should work.

OK, I see now. I did not consider the case where people would like to
get upgrade from a dev version of 9.6 to the latest 9.6 version, just
the upgrade from a previous major version <= 9.5. Thanks for reminding
that pg_upgrade needs to support that.

Pushed with Bruce's original patch included.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#75Bruce Momjian
bruce@momjian.us
In reply to: Bruce Momjian (#72)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Thu, Feb 11, 2016 at 07:18:46PM -0500, Bruce Momjian wrote:

No, that is not an improvement --- see my previous comment:

We could get more sophisticated by checking the catalog version number
where the format was changed, but that doesn't seem worth it, and is
overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

By testing for '906', you prevent users from using pg_upgrade to go from
one catalog version of 9.6 to a later one. Few people may want to do
it, but it should work.

C comment added explaining why we do this.

--
Bruce Momjian <bruce@momjian.us> http://momjian.us
EnterpriseDB http://enterprisedb.com

+ As you are, so once was I. As I am, so you will be. +
+ Roman grave inscription                             +

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#76Michael Paquier
michael.paquier@gmail.com
In reply to: Bruce Momjian (#75)
Re: NextXID format change (was Re: exposing pg_controldata and pg_config as functions)

On Sat, Feb 13, 2016 at 7:54 AM, Bruce Momjian <bruce@momjian.us> wrote:

On Thu, Feb 11, 2016 at 07:18:46PM -0500, Bruce Momjian wrote:

No, that is not an improvement --- see my previous comment:

We could get more sophisticated by checking the catalog version number
where the format was changed, but that doesn't seem worth it, and is
overly complex because we get the catalog version number from
pg_controldata, so you would be adding a dependency in ordering of the
pg_controldata entries.

By testing for '906', you prevent users from using pg_upgrade to go from
one catalog version of 9.6 to a later one. Few people may want to do
it, but it should work.

C comment added explaining why we do this.

Thanks for the addition.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#77Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#68)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 01/20/2016 08:08 PM, Michael Paquier wrote:

On Wed, Jan 20, 2016 at 2:32 AM, Joe Conway <mail@joeconway.com> wrote:

The only things I know of still lacking is:
1) Documentation

Done and included in the attached.

2) Decision on REVOKE ... FROM PUBLIC

Yep, regarding 2) I am the only one actually making noise to protect
this information by default, against at least 2 committers :)

I plan to commit this way -- if the decision is made to remove the two
REVOKEs it can always be done later, but I see no problem with it.

+typedef struct configdata
+{
+       char       *name;
+       char       *setting;
+} configdata;
For a better analogy to ControlFileData, this could be renamed ConfigData?

Well I was already using ConfigData as the variable name, but after some
review it seems better your way, so I made the struct ConfigData and the
variable configdata.

The point of the move to src/common is to remove the duplication in
src/bin/pg_config/Makefile.

check

All the files in src/common should begin their include declarations with that:
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

check

+configdata *
+get_configdata(char *my_exec_path, size_t *configdata_len)
+{
It may be good to mention that the result is palloc'd and that caller
may need to free it if necessary. It does not matter in the two code
paths of this patch, but it may matter for other users calling that.

check

I believe this takes care of all open issues with this, so I plan to
commit it as attached in a day or two. Thanks for your reviews and comments!

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_config-2016.02.15.00.difftext/x-diff; name=pg_config-2016.02.15.00.diffDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 412c845..6c50f79 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 7350,7355 ****
--- 7350,7360 ----
       </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>
       </row>
***************
*** 7609,7614 ****
--- 7614,7667 ----
    </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 PostgreSQL. It is intended, for example, to be used by
+    software packages that want to interface to PostgreSQL to facilitate
+    finding the required header files and libraries. It provides the same
+    basic information as the <xref linkend="app-pgconfig"> PostgreSQL Client
+    Application. There is a System Information Function
+    (<xref linkend="functions-info">) called <function>pg_config</function>
+    which underlies this view.
+   </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/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f9eea76..29c36d2 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT * FROM pg_ls_dir('.') WITH ORDINA
*** 15003,15008 ****
--- 15003,15014 ----
        </row>
  
        <row>
+        <entry><literal><function>pg_config()</function></literal></entry>
+        <entry><type>setof record</type></entry>
+        <entry>get list of compile-time configure variable names and their values</entry>
+       </row>
+ 
+       <row>
         <entry><literal><function>pg_is_other_temp_schema(<type>oid</type>)</function></literal></entry>
         <entry><type>boolean</type></entry>
         <entry>is schema another session's temporary schema?</entry>
*************** SET search_path TO <replaceable>schema</
*** 15245,15250 ****
--- 15251,15273 ----
     </para>
  
     <indexterm>
+     <primary>pg_config</primary>
+    </indexterm>
+ 
+    <para>
+     <function>pg_config</function> returns a set of records describing the
+     compile-time configuration parameters of the currently installed
+     version of PostgreSQL. It is intended, for example, to be used by
+     software packages that want to interface to PostgreSQL to facilitate
+     finding the required header files and libraries. The
+     <structfield>name</> column contains the parameter name.
+     The <structfield>setting</> column contains the parameter value. It
+     provides the same basic information as the
+     <xref linkend="app-pgconfig"> PostgreSQL Client Application. There
+     is also a <xref linkend="view-pg-config"> system view.
+    </para>
+ 
+    <indexterm>
      <primary>version</primary>
     </indexterm>
  
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..abf9a70 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..a0c82c1 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
index ...a74e97d .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 0 ****
--- 1,102 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_config.c
+  *		Expose same output as pg_config except as an SRF
+  *
+  * Portions Copyright (c) 1996-2015, 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 812c4a1..26fbaad 100644
*** a/src/bin/pg_config/Makefile
--- b/src/bin/pg_config/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 17,36 ****
  
  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
--- 17,22 ----
diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b14294..23c0495 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 25,387 ****
  #include "postgres_fe.h"
  
  #include "port.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
--- 25,33 ----
  #include "postgres_fe.h"
  
  #include "port.h"
+ #include "common/config_info.h"
  
  static const char *progname;
  
  /*
   * Table of known information items
*************** show_version(bool all)
*** 391,423 ****
  typedef struct
  {
  	const char *switchname;
! 	void		(*show_func) (bool all);
  } 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},
  	{NULL, NULL}
  };
  
--- 37,69 ----
  typedef struct
  {
  	const char *switchname;
! 	const char *configname;
  } InfoItem;
  
  static const InfoItem info_items[] = {
! 	{"--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}
  };
  
*************** advice(void)
*** 466,487 ****
  }
  
  static void
! show_all(void)
  {
  	int			i;
  
! 	for (i = 0; info_items[i].switchname != NULL; i++)
  	{
! 		(*info_items[i].show_func) (true);
  	}
  }
  
  int
  main(int argc, char **argv)
  {
  	int			i;
  	int			j;
- 	int			ret;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
--- 112,138 ----
  }
  
  static void
! show_item(const char *configname,
! 		  ConfigData *configdata,
! 		  size_t configdata_len)
  {
  	int			i;
  
! 	for (i = 0; i < configdata_len; i++)
  	{
! 		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;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
*************** main(int argc, char **argv)
*** 497,524 ****
  		}
  	}
  
! 	ret = find_my_exec(argv[0], mypath);
! 
! 	if (ret)
  	{
  		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
  		exit(1);
  	}
  
  	/* no arguments -> print everything */
  	if (argc < 2)
  	{
! 		show_all();
  		exit(0);
  	}
  
  	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);
  				break;
  			}
  		}
--- 148,177 ----
  		}
  	}
  
! 	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)
  	{
! 		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)
  			{
! 				show_item(info_items[j].configname,
! 						  configdata, configdata_len);
  				break;
  			}
  		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..ab183cf 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,43 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! # 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 = exec.o config_info.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
  
*************** libpgcommon_srv.a: $(OBJS_SRV)
*** 61,67 ****
  # 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 $@
  
  $(OBJS_SRV): | submake-errcodes
  
--- 74,80 ----
  # 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 $@
  
  $(OBJS_SRV): | submake-errcodes
  
diff --git a/src/common/config_info.c b/src/common/config_info.c
index ...1e3c6db .
*** a/src/common/config_info.c
--- b/src/common/config_info.c
***************
*** 0 ****
--- 1,206 ----
+ /*-------------------------------------------------------------------------
+  *
+  * config_info.c
+  *		Common code for pg_config output
+  *
+  *
+  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.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/pg_proc.h b/src/include/catalog/pg_proc.h
index 1c0ef9a..f4c9572 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("get an individual replication ori
*** 5202,5207 ****
--- 5202,5210 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* 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
diff --git a/src/include/common/config_info.h b/src/include/common/config_info.h
index ...488fcf1 .
*** a/src/include/common/config_info.h
--- b/src/include/common/config_info.h
***************
*** 0 ****
--- 1,21 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Copyright (c) 2016, PostgreSQL Global Development Group
+  *
+  *	src/include/common/controldata_utils.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 9fc79f4..cb13dd8 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 affcc01..a784de9 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1147,1152 ****
--- 1147,1155 ----
  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 a418f93..5c9de0c 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2bdba2d..81bc5c9 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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 1dba7d9..cc0dcb3 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,113 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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
--- 106,113 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  exec.c config_info.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
#78Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#77)
Re: exposing pg_controldata and pg_config as functions

On Tue, Feb 16, 2016 at 5:29 AM, Joe Conway <mail@joeconway.com> wrote:

I believe this takes care of all open issues with this, so I plan to
commit it as attached in a day or two. Thanks for your reviews and comments!

Here are just a couple of cosmetic comments.

+   The view <structname>pg_config</structname> describes the
+   compile-time configuration parameters of the currently installed
+   version of PostgreSQL. It is intended, for example, to be used by
+   software packages that want to interface to PostgreSQL to facilitate
+   finding the required header files and libraries. It provides the same
+   basic information as the <xref linkend="app-pgconfig"> PostgreSQL Client
+   Application. There is a System Information Function
Missing markup <productname></> around PostgreSQL.

+ Application. There is a System Information Function
Why is using upper-case characters necessary here? This could just say
system function.

The paragraph in func.sgml is a copy-paste of the one in
catalogs.sgml. We may want to remove the duplication.

+   /* let the caller know we're sending back a tuplestore */
+   rsinfo->returnMode = SFRM_Materialize;
I guess one can recognize your style here for SRF functions :)
@@ -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 $@
Diff noise?
--- /dev/null
+++ b/src/common/config_info.c
[...]
+ * IDENTIFICATION
+ *   src/common/controldata_utils.c
This is incorrect.
+ * IDENTIFICATION
+ *   src/backend/utils/misc/pg_config.c
+ *
+ */
I am nitpicking here but this header block should have a long
"----------------" at its bottom.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#79Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#78)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 02/15/2016 11:20 PM, Michael Paquier wrote:

Here are just a couple of cosmetic comments.

Missing markup <productname></> around PostgreSQL.

Added, but I'll note that there are tons of locations in the docs where
we do not do that. Maybe they should all be made consistent.

+ Application. There is a System Information Function
Why is using upper-case characters necessary here? This could just say
system function.

It is capitalized because it refers to a section in the docs. I followed
it with <xref linkend="functions-info"> as well, so it ends up reading:
"There is a System Information Function (Section 9.25) called pg_config
which underlies this view."

I guess I could be convinced to lower case it, but I thought this looked
better.

The paragraph in func.sgml is a copy-paste of the one in
catalogs.sgml. We may want to remove the duplication.

The paragraphs are mostly copy-paste, but slightly different (toward the
end). There is both a function and a system view -- why would we not
want a description in both places?

+   /* let the caller know we're sending back a tuplestore */
+   rsinfo->returnMode = SFRM_Materialize;
I guess one can recognize your style here for SRF functions :)

Old habits die hard ;-)

@@ -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 $@
Diff noise?

No, intentional. The original version leaves a white space at the
beginning of CPPFLAGS after removing -DFRONTEND.

--- /dev/null
+++ b/src/common/config_info.c
[...]
+ * IDENTIFICATION
+ *   src/common/controldata_utils.c
This is incorrect.

Right -- found and fixed several of these with Alvaro's help after
posting. Also fixed Copyright dates (s/2015/2016/) on the new files.

+ * IDENTIFICATION
+ *   src/backend/utils/misc/pg_config.c
+ *
+ */
I am nitpicking here but this header block should have a long
"----------------" at its bottom.

Fair enough -- fixed.

Thanks!

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_config-2016.02.16.01.difftext/x-diff; name=pg_config-2016.02.16.01.diffDownload
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 412c845..7b71768 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 7350,7355 ****
--- 7350,7360 ----
       </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>
       </row>
***************
*** 7609,7614 ****
--- 7614,7668 ----
    </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. There is a System Information Function
+    (<xref linkend="functions-info">) called <function>pg_config</function>
+    which underlies this view.
+   </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/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f9eea76..0e17ca3 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT * FROM pg_ls_dir('.') WITH ORDINA
*** 15003,15008 ****
--- 15003,15014 ----
        </row>
  
        <row>
+        <entry><literal><function>pg_config()</function></literal></entry>
+        <entry><type>setof record</type></entry>
+        <entry>get list of compile-time configure variable names and their values</entry>
+       </row>
+ 
+       <row>
         <entry><literal><function>pg_is_other_temp_schema(<type>oid</type>)</function></literal></entry>
         <entry><type>boolean</type></entry>
         <entry>is schema another session's temporary schema?</entry>
*************** SET search_path TO <replaceable>schema</
*** 15245,15250 ****
--- 15251,15274 ----
     </para>
  
     <indexterm>
+     <primary>pg_config</primary>
+    </indexterm>
+ 
+    <para>
+     <function>pg_config</function> returns a set of records describing 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. The <structfield>name</> column contains the
+     parameter name. The <structfield>setting</> column contains the
+     parameter value. It provides the same basic information as the
+     <xref linkend="app-pgconfig"> <productname>PostgreSQL</> Client
+     Application. There is also a <xref linkend="view-pg-config"> system
+     view.
+    </para>
+ 
+    <indexterm>
      <primary>version</primary>
     </indexterm>
  
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 923fe58..abf9a70 100644
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
*************** CREATE VIEW pg_timezone_abbrevs AS
*** 433,438 ****
--- 433,444 ----
  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 7889101..a0c82c1 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,21 ****
  
  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
  
  # This location might depend on the installation directories. Therefore
  # we can't subsitute it into pg_config.h.
--- 14,21 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
index ...3d6b9f2 .
*** a/src/backend/utils/misc/pg_config.c
--- b/src/backend/utils/misc/pg_config.c
***************
*** 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 812c4a1..26fbaad 100644
*** a/src/bin/pg_config/Makefile
--- b/src/bin/pg_config/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 17,36 ****
  
  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
--- 17,22 ----
diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c
index 4b14294..23c0495 100644
*** a/src/bin/pg_config/pg_config.c
--- b/src/bin/pg_config/pg_config.c
***************
*** 25,387 ****
  #include "postgres_fe.h"
  
  #include "port.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
--- 25,33 ----
  #include "postgres_fe.h"
  
  #include "port.h"
+ #include "common/config_info.h"
  
  static const char *progname;
  
  /*
   * Table of known information items
*************** show_version(bool all)
*** 391,423 ****
  typedef struct
  {
  	const char *switchname;
! 	void		(*show_func) (bool all);
  } 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},
  	{NULL, NULL}
  };
  
--- 37,69 ----
  typedef struct
  {
  	const char *switchname;
! 	const char *configname;
  } InfoItem;
  
  static const InfoItem info_items[] = {
! 	{"--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}
  };
  
*************** advice(void)
*** 466,487 ****
  }
  
  static void
! show_all(void)
  {
  	int			i;
  
! 	for (i = 0; info_items[i].switchname != NULL; i++)
  	{
! 		(*info_items[i].show_func) (true);
  	}
  }
  
  int
  main(int argc, char **argv)
  {
  	int			i;
  	int			j;
- 	int			ret;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
--- 112,138 ----
  }
  
  static void
! show_item(const char *configname,
! 		  ConfigData *configdata,
! 		  size_t configdata_len)
  {
  	int			i;
  
! 	for (i = 0; i < configdata_len; i++)
  	{
! 		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;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_config"));
  
*************** main(int argc, char **argv)
*** 497,524 ****
  		}
  	}
  
! 	ret = find_my_exec(argv[0], mypath);
! 
! 	if (ret)
  	{
  		fprintf(stderr, _("%s: could not find own program executable\n"), progname);
  		exit(1);
  	}
  
  	/* no arguments -> print everything */
  	if (argc < 2)
  	{
! 		show_all();
  		exit(0);
  	}
  
  	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);
  				break;
  			}
  		}
--- 148,177 ----
  		}
  	}
  
! 	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)
  	{
! 		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)
  			{
! 				show_item(info_items[j].configname,
! 						  configdata, configdata_len);
  				break;
  			}
  		}
diff --git a/src/common/Makefile b/src/common/Makefile
index c47445e..ab183cf 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 23,30 ****
  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
  
  OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o restricted_token.o
  
--- 23,43 ----
  override CPPFLAGS := -DFRONTEND $(CPPFLAGS)
  LIBS += $(PTHREAD_LIBS)
  
! # 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 = exec.o config_info.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
  
*************** libpgcommon_srv.a: $(OBJS_SRV)
*** 61,67 ****
  # 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 $@
  
  $(OBJS_SRV): | submake-errcodes
  
--- 74,80 ----
  # 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 $@
  
  $(OBJS_SRV): | submake-errcodes
  
diff --git a/src/common/config_info.c b/src/common/config_info.c
index ...9053a8c .
*** a/src/common/config_info.c
--- b/src/common/config_info.c
***************
*** 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 378c40f..3b3bd17 100644
*** a/src/include/catalog/catversion.h
--- b/src/include/catalog/catversion.h
***************
*** 53,58 ****
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	201602071
  
  #endif
--- 53,58 ----
   */
  
  /*							yyyymmddN */
! #define CATALOG_VERSION_NO	201602161
  
  #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b24e434..2222e8f 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("row security for current context
*** 5208,5213 ****
--- 5208,5217 ----
  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
index ...649ef5c .
*** a/src/include/common/config_info.h
--- b/src/include/common/config_info.h
***************
*** 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 9fc79f4..cb13dd8 100644
*** a/src/include/port.h
--- b/src/include/port.h
*************** extern void join_path_components(char *r
*** 42,47 ****
--- 42,48 ----
  					 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 affcc01..a784de9 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum set_config_by_name(PG_FUNCT
*** 1147,1152 ****
--- 1147,1155 ----
  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 a418f93..5c9de0c 100644
*** a/src/port/path.c
--- b/src/port/path.c
*************** make_native_path(char *filename)
*** 172,177 ****
--- 172,207 ----
  
  
  /*
+  * 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
   *
   * We omit the slash if either given component is empty.
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2bdba2d..81bc5c9 100644
*** a/src/test/regress/expected/rules.out
--- b/src/test/regress/expected/rules.out
*************** pg_available_extensions| SELECT e.name,
*** 1305,1310 ****
--- 1305,1313 ----
      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 1dba7d9..e4fb44e 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,113 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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
--- 106,113 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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
#80Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#79)
Re: exposing pg_controldata and pg_config as functions

On Tue, Feb 16, 2016 at 11:36 PM, Joe Conway <mail@joeconway.com> wrote:

Thanks!

OK. I think I'm good now. Thanks for the quick update.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#81Michael Paquier
michael.paquier@gmail.com
In reply to: Michael Paquier (#80)
Re: exposing pg_controldata and pg_config as functions

On Wed, Feb 17, 2016 at 10:13 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:

On Tue, Feb 16, 2016 at 11:36 PM, Joe Conway <mail@joeconway.com> wrote:

Thanks!

OK. I think I'm good now. Thanks for the quick update.

Actually, having second-thoughts on the matter, why is is that
necessary to document the function pg_config()? The functions wrapping
a system view just have the view documented, take for example
pg_show_all_file_settings, pg_show_all_settings,
pg_stat_get_wal_receiver, etc.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#82Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#81)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 02:32 AM, Michael Paquier wrote:

Actually, having second-thoughts on the matter, why is is that
necessary to document the function pg_config()? The functions wrapping
a system view just have the view documented, take for example
pg_show_all_file_settings, pg_show_all_settings,
pg_stat_get_wal_receiver, etc.

Ok, removed the documentation on the function pg_config() and pushed.
Included bumped catversion.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#83Peter Eisentraut
peter_e@gmx.net
In reply to: Michael Paquier (#69)
Re: exposing pg_controldata and pg_config as functions

On 1/31/16 7:34 AM, Michael Paquier wrote:

I am marking this patch as returned with feedback for now, not all the
issues have been fixed yet, and there are still no docs (the
conclusion being that people would like to have this stuff, right?).
Feel free to move it to the next CF should a new version be written.

I think we still don't have a real use case for this feature, and a
couple of points against it.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#84Peter Eisentraut
peter_e@gmx.net
In reply to: Joe Conway (#82)
Re: exposing pg_controldata and pg_config as functions

On 2/17/16 12:15 PM, Joe Conway wrote:

On 02/17/2016 02:32 AM, Michael Paquier wrote:

Actually, having second-thoughts on the matter, why is is that
necessary to document the function pg_config()? The functions wrapping
a system view just have the view documented, take for example
pg_show_all_file_settings, pg_show_all_settings,
pg_stat_get_wal_receiver, etc.

Ok, removed the documentation on the function pg_config() and pushed.

I still have my serious doubts about this, especially not even requiring
superuser access for this information. Could someone explain why we
need this?

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#85Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#84)
Re: exposing pg_controldata and pg_config as functions

Peter Eisentraut <peter_e@gmx.net> writes:

On 2/17/16 12:15 PM, Joe Conway wrote:

Ok, removed the documentation on the function pg_config() and pushed.

I still have my serious doubts about this, especially not even requiring
superuser access for this information. Could someone explain why we
need this?

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#86Joe Conway
mail@joeconway.com
In reply to: Tom Lane (#85)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 02:14 PM, Tom Lane wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

On 2/17/16 12:15 PM, Joe Conway wrote:

Ok, removed the documentation on the function pg_config() and pushed.

I still have my serious doubts about this, especially not even requiring
superuser access for this information. Could someone explain why we
need this?

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

It does not let anyone see config data out of the box:

+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+
+ REVOKE ALL on pg_config FROM PUBLIC;
+ REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;
+

But it does not have an explicit superuser check. I can add that if
that's the consensus.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#87Josh berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 01:31 PM, Peter Eisentraut wrote:

On 1/31/16 7:34 AM, Michael Paquier wrote:

I am marking this patch as returned with feedback for now, not all the
issues have been fixed yet, and there are still no docs (the
conclusion being that people would like to have this stuff, right?).
Feel free to move it to the next CF should a new version be written.

I think we still don't have a real use case for this feature, and a
couple of points against it.

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata. That leaves no
reasonable way to expose this information in an API.

(and yes, we have a bigger issue with stuff which is only in pg_log, but
one thing at a time)

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#88Andrew Dunstan
andrew@dunslane.net
In reply to: Tom Lane (#85)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 05:14 PM, Tom Lane wrote:

Peter Eisentraut <peter_e@gmx.net> writes:

On 2/17/16 12:15 PM, Joe Conway wrote:

Ok, removed the documentation on the function pg_config() and pushed.

I still have my serious doubts about this, especially not even requiring
superuser access for this information. Could someone explain why we
need this?

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

I'm in favor, and don't really want to rehearse the argument. But I
think I can live quite happily with it being superuser only. It's pretty
hard to argue that exposing it to a superuser creates much risk, ISTM.

cheers

andrew

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#89Tom Lane
tgl@sss.pgh.pa.us
In reply to: Joe Conway (#86)
Re: exposing pg_controldata and pg_config as functions

Joe Conway <mail@joeconway.com> writes:

On 02/17/2016 02:14 PM, Tom Lane wrote:

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

It does not let anyone see config data out of the box:

+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+
+ REVOKE ALL on pg_config FROM PUBLIC;
+ REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;

Ah, that's fine. I'd looked for a superuser() check and not seen one,
but letting the SQL permissions system handle it seems good enough.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#90Josh berkus
josh@agliodbs.com
In reply to: Andrew Dunstan (#1)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 03:02 PM, Tom Lane wrote:

Joe Conway <mail@joeconway.com> writes:

On 02/17/2016 02:14 PM, Tom Lane wrote:

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

It does not let anyone see config data out of the box:

+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+
+ REVOKE ALL on pg_config FROM PUBLIC;
+ REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;

Ah, that's fine. I'd looked for a superuser() check and not seen one,
but letting the SQL permissions system handle it seems good enough.

What I like about this is that if I want to expose it to a
non-superuser, I can just do a GRANT instead of needing to write a
security definer view.

--
--
Josh Berkus
Red Hat OSAS
(any opinions are my own)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#91Joe Conway
mail@joeconway.com
In reply to: Josh berkus (#90)
Re: exposing pg_controldata and pg_config as functions

On 02/17/2016 03:34 PM, Josh berkus wrote:

On 02/17/2016 03:02 PM, Tom Lane wrote:

Joe Conway <mail@joeconway.com> writes:

On 02/17/2016 02:14 PM, Tom Lane wrote:

I thought we'd agreed on requiring superuser access for this function.
I concur that letting just anyone see the config data is inappropriate.

It does not let anyone see config data out of the box:

+ CREATE VIEW pg_config AS
+     SELECT * FROM pg_config();
+
+ REVOKE ALL on pg_config FROM PUBLIC;
+ REVOKE EXECUTE ON FUNCTION pg_config() FROM PUBLIC;

Ah, that's fine. I'd looked for a superuser() check and not seen one,
but letting the SQL permissions system handle it seems good enough.

What I like about this is that if I want to expose it to a
non-superuser, I can just do a GRANT instead of needing to write a
security definer view.

Which was my reason for doing it this way, although that GRANT will not
get preserved by pg_dump currently. Stephen Frost is working on a patch
to change/fix that though (see the "Additional role attributes &&
superuser review" thread), which I believe he intends to get done RSN
and into 9.6.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#92Peter Eisentraut
peter_e@gmx.net
In reply to: Josh berkus (#87)
Re: exposing pg_controldata and pg_config as functions

On 2/17/16 5:20 PM, Josh berkus wrote:

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata.

I'm talking about the pg_config() function, not pg_controldata.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#93Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Eisentraut (#92)
Re: exposing pg_controldata and pg_config as functions

On Thu, Feb 18, 2016 at 11:02 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/17/16 5:20 PM, Josh berkus wrote:

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata.

I'm talking about the pg_config() function, not pg_controldata.

Andrew has mentioned a use case he had at the beginning of this thread
to enhance a bit the regression tests related to libxml.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#94Peter Eisentraut
peter_e@gmx.net
In reply to: Michael Paquier (#93)
Re: exposing pg_controldata and pg_config as functions

On 2/17/16 9:08 PM, Michael Paquier wrote:

On Thu, Feb 18, 2016 at 11:02 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/17/16 5:20 PM, Josh berkus wrote:

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata.

I'm talking about the pg_config() function, not pg_controldata.

Andrew has mentioned a use case he had at the beginning of this thread
to enhance a bit the regression tests related to libxml.

While that idea was useful, I think we had concluded that there are
better ways to do this and that this way probably wouldn't even work
(Windows?).

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#95Andres Freund
andres@anarazel.de
In reply to: Peter Eisentraut (#94)
Re: exposing pg_controldata and pg_config as functions

On 2016-02-17 21:19:08 -0500, Peter Eisentraut wrote:

On 2/17/16 9:08 PM, Michael Paquier wrote:

On Thu, Feb 18, 2016 at 11:02 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/17/16 5:20 PM, Josh berkus wrote:

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata.

I'm talking about the pg_config() function, not pg_controldata.

Andrew has mentioned a use case he had at the beginning of this thread
to enhance a bit the regression tests related to libxml.

While that idea was useful, I think we had concluded that there are
better ways to do this and that this way probably wouldn't even work
(Windows?).

I don't understand why you're so opposed to this. Several people said
that they're interested in this information in the current discussion
and it has been requested repeatedly over the years. For superusers you
can already hack access, but it's darn ugly.

Greetings,

Andres Freund

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#96Joe Conway
mail@joeconway.com
In reply to: Andres Freund (#95)
Re: exposing pg_controldata and pg_config as functions

On 02/18/2016 12:30 AM, Andres Freund wrote:

On 2016-02-17 21:19:08 -0500, Peter Eisentraut wrote:

On 2/17/16 9:08 PM, Michael Paquier wrote:

On Thu, Feb 18, 2016 at 11:02 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/17/16 5:20 PM, Josh berkus wrote:

I have a use-case for this feature, at part of it containerized
PostgreSQL. Right now, there is certain diagnostic information (like
timeline) which is exposed ONLY in pg_controldata.

I'm talking about the pg_config() function, not pg_controldata.

Andrew has mentioned a use case he had at the beginning of this thread
to enhance a bit the regression tests related to libxml.

While that idea was useful, I think we had concluded that there are
better ways to do this and that this way probably wouldn't even work
(Windows?).

Just to be clear, AFAIK there is no issue with the committed changes on
Windows. If there is please provide a concrete example that we can discuss.

I don't understand why you're so opposed to this. Several people said
that they're interested in this information in the current discussion
and it has been requested repeatedly over the years. For superusers you
can already hack access, but it's darn ugly.

Exactly.

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

#97Peter Eisentraut
peter_e@gmx.net
In reply to: Andres Freund (#95)
Re: exposing pg_controldata and pg_config as functions

On 2/18/16 3:30 AM, Andres Freund wrote:

I don't understand why you're so opposed to this. Several people said
that they're interested in this information in the current discussion
and it has been requested repeatedly over the years.

I think no one except Andrew Dunstan has requested this, and his use
case is disputed. Everyone else is either confusing this with the
pg_controldata part or is just transitively claiming that someone else
wanted it.

I don't have a problem with the implementation, but I don't understand
what this feature is meant for.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#98Peter Eisentraut
peter_e@gmx.net
In reply to: Joe Conway (#96)
Re: exposing pg_controldata and pg_config as functions

On 2/18/16 12:20 PM, Joe Conway wrote:

Just to be clear, AFAIK there is no issue with the committed changes on
Windows. If there is please provide a concrete example that we can discuss.

Originally it was mentioned that this feature could be used in the
regression tests by making certain tests conditional on configure
options. Which presumably won't work if the build was on Windows.

I don't doubt that your code actually works on Windows.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#99Michael Paquier
michael.paquier@gmail.com
In reply to: Peter Eisentraut (#98)
Re: exposing pg_controldata and pg_config as functions

On Fri, Feb 19, 2016 at 11:53 AM, Peter Eisentraut <peter_e@gmx.net> wrote:

On 2/18/16 12:20 PM, Joe Conway wrote:

Just to be clear, AFAIK there is no issue with the committed changes on
Windows. If there is please provide a concrete example that we can discuss.

Originally it was mentioned that this feature could be used in the
regression tests by making certain tests conditional on configure
options. Which presumably won't work if the build was on Windows.

MSVC code passes VAL_CONFIGURE to pg_config.h by calling
GetFakeConfigure() and make the output of pg_config consistent with
when ./configure is used. So for CONFIGURE I see no issues. Things
like CPPFLAGS or LIBS though become listed as "not recorded" with this
change so the output of pg_config is more verbose when MSVC is used.
This still seems an acceptable trade-off even after reviewing this
patch to make this information available on the backend. And it seems
as well that this would become set, at least partially, when using
cmake build instead of the MSVC cruft in src/tools/msvc.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#100Joe Conway
mail@joeconway.com
In reply to: Joe Conway (#48)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 01/17/2016 04:10 PM, Joe Conway wrote:

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

3) Adds new functions, more or less in line with previous discussions:
* pg_checkpoint_state()
* pg_controldata_state()
* pg_recovery_state()
* pg_init_state()

Taking the opposite direction of Josh upthread, why is this split
actually necessary? Isn't the idea to provide a SQL interface of what
pg_controldata shows? If this split proves to be useful, shouldn't we
do it as well for pg_controldata?

The last discussion moved strongly in the direction of separate
functions, and that being different from pg_controldata was not a bad
thing. That said, I'm still of the opinion that there are legitimate
reasons to want the command line pg_controldata and the SQL functions to
produce equivalent, if not identical, results. I just wish we could get
a clear consensus one way or the other.

I've assumed that we are sticking with the separate functions. As such,
here is a rebased patch, with documentation and other fixes such as
Copyright year, Mkvcbuild support, and some cruft removal.

I think that those functions should be superuser-only. They provide
information about the system globally.

The tail of this thread seems to be headed away from this direction.
I'll take another look and propose something concrete.

I've looked at existing functions that seem similar, and as far as I can
see none are superuser-only. I'm certainly happy to make them so if
that's the consensus, but currently they are wide open. Opinions?

For convenience in answering that question, here is what information is
included in the output of each function (\df so you can see the data
types, plus SELECT output for a more readable example):

8<-------------------------
postgres=# \x
Expanded display is on.

postgres=# \df pg_checkpoint_state
Name | pg_checkpoint_state
Result data type | record
Argument data types | OUT checkpoint_location pg_lsn, OUT prior_location
pg_lsn, OUT redo_location pg_lsn, OUT redo_wal_file text, OUT
timeline_id integer, OUT prev_timeline_id integer, OUT full_page_writes
boolean, OUT next_xid text, OUT next_oid oid, OUT next_multixact_id xid,
OUT next_multi_offset xid, OUT oldest_xid xid, OUT oldest_xid_dbid oid,
OUT oldest_active_xid xid, OUT oldest_multi_xid xid, OUT
oldest_multi_dbid oid, OUT oldest_commit_ts_xid xid, OUT
newest_commit_ts_xid xid, OUT checkpoint_time timestamp with time zone

postgres=# select * from pg_checkpoint_state();
-[ RECORD 1 ]--------+-------------------------
checkpoint_location | 0/14CD368
prior_location | 0/14CD0D0
redo_location | 0/14CD368
redo_wal_file | 000000010000000000000001
timeline_id | 1
prev_timeline_id | 1
full_page_writes | t
next_xid | 0:576
next_oid | 12415
next_multixact_id | 1
next_multi_offset | 0
oldest_xid | 568
oldest_xid_dbid | 1
oldest_active_xid | 0
oldest_multi_xid | 1
oldest_multi_dbid | 1
oldest_commit_ts_xid | 0
newest_commit_ts_xid | 0
checkpoint_time | 2016-02-19 18:44:51-08

postgres=# \df pg_controldata_state
Name | pg_controldata_state
Result data type | record
Argument data types | OUT pg_control_version integer, OUT
catalog_version_no integer, OUT system_identifier bigint, OUT
pg_control_last_modified timestamp with time zone

postgres=# select * from pg_controldata_state();
-[ RECORD 1 ]------------+-----------------------
pg_control_version | 942
catalog_version_no | 201602171
system_identifier | 6253198751269127743
pg_control_last_modified | 2016-02-19 18:44:58-08

postgres=# \df pg_init_state
Name | pg_init_state
Result data type | record
Argument data types | OUT max_data_alignment integer, OUT
database_block_size integer, OUT blocks_per_segment integer, OUT
wal_block_size integer, OUT bytes_per_wal_segment integer, OUT
max_identifier_length integer, OUT max_index_columns integer, OUT
max_toast_chunk_size integer, OUT large_object_chunk_size integer, OUT
bigint_timestamps boolean, OUT float4_pass_by_value boolean, OUT
float8_pass_by_value boolean, OUT data_page_checksum_version integer

postgres=# select * from pg_init_state();
-[ RECORD 1 ]--------------+---------
max_data_alignment | 8
database_block_size | 8192
blocks_per_segment | 131072
wal_block_size | 8192
bytes_per_wal_segment | 16777216
max_identifier_length | 64
max_index_columns | 32
max_toast_chunk_size | 1996
large_object_chunk_size | 2048
bigint_timestamps | t
float4_pass_by_value | t
float8_pass_by_value | t
data_page_checksum_version | 0

postgres=# \df pg_recovery_state
Result data type | record
Argument data types | OUT min_recovery_end_location pg_lsn, OUT
min_recovery_end_timeline integer, OUT backup_start_location pg_lsn, OUT
backup_end_location pg_lsn, OUT end_of_backup_record_required boolean

postgres=# select * from pg_recovery_state();
-[ RECORD 1 ]-----------------+----
min_recovery_end_location | 0/0
min_recovery_end_timeline | 0
backup_start_location | 0/0
backup_end_location | 0/0
end_of_backup_record_required | f
8<-------------------------

Is there general consensus that we want this feature, and that we want
it in this form? Any other comments?

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_controldata_funcs-2016.02.19.04.difftext/x-diff; name=pg_controldata_funcs-2016.02.19.04.diffDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f9eea76..c03b59f 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT collation for ('foo' COLLATE "de_
*** 16674,16679 ****
--- 16674,16789 ----
      </tgroup>
     </table>
  
+    <para>
+     The functions shown in <xref linkend="functions-controldata">
+     print information initialized during <command>initdb</>, such
+     as the catalog version. They also show information about write-ahead
+     logging and checkpoint processing. This information is cluster-wide,
+     and not specific to any one database. They provide most of the same
+     information, from the same source, as
+     <xref linkend="APP-PGCONTROLDATA">, although in a form better suited
+     to <acronym>SQL</acronym> functions.
+    </para>
+ 
+    <table id="functions-controldata">
+     <title>Control Data Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <indexterm><primary>pg_checkpoint_state</primary></indexterm>
+         <literal><function>pg_checkpoint_state()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current checkpoint state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_controldata_state</primary></indexterm>
+         <literal><function>pg_controldata_state()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current controldata file state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_init_state</primary></indexterm>
+         <literal><function>pg_init_state()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about cluster initialization state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_recovery_state</primary></indexterm>
+         <literal><function>pg_recovery_state()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about recovery state.
+        </entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <indexterm>
+     <primary>pg_checkpoint_state</primary>
+    </indexterm>
+    <para>
+     <function>pg_checkpoint_state</> returns a record containing
+     checkpoint_location, prior_location, redo_location, redo_wal_file,
+     timeline_id, prev_timeline_id, full_page_writes, next_xid, next_oid,
+     next_multixact_id, next_multi_offset, oldest_xid, oldest_xid_dbid,
+     oldest_active_xid, oldest_multi_xid, oldest_multi_dbid,
+     oldest_commit_ts_xid, newest_commit_ts_xid, and checkpoint_time.
+    </para>
+ 
+    <indexterm>
+     <primary>pg_controldata_state</primary>
+    </indexterm>
+    <para>
+     <function>pg_controldata_state</> returns a record containing
+     pg_control_version, catalog_version_no, system_identifier, and
+     pg_control_last_modified.
+    </para>
+ 
+    <indexterm>
+     <primary>pg_init_state</primary>
+    </indexterm>
+    <para>
+     <function>pg_init_state</> returns a record containing
+     max_data_alignment, database_block_size, blocks_per_segment,
+     wal_block_size, bytes_per_wal_segment, max_identifier_length,
+     max_index_columns, max_toast_chunk_size, large_object_chunk_size,
+     bigint_timestamps, float4_pass_by_value, float8_pass_by_value, and
+     data_page_checksum_version.
+    </para>
+ 
+    <indexterm>
+     <primary>pg_recovery_state</primary>
+    </indexterm>
+    <para>
+     <function>pg_recovery_state</> returns a record containing
+     min_recovery_end_location, min_recovery_end_timeline,
+     backup_start_location, backup_end_location, and
+     end_of_backup_record_required.
+    </para>
+ 
    </sect1>
  
    <sect1 id="functions-admin">
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index a0c82c1..a5b487d 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,20 ****
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
--- 14,20 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! OBJS = guc.o help_config.o pg_config.o pg_controldata.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
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index ...489bf67 .
*** a/src/backend/utils/misc/pg_controldata.c
--- b/src/backend/utils/misc/pg_controldata.c
***************
*** 0 ****
--- 1,358 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_controldata.c
+  *		Expose select pg_controldata output, except via SQL functions
+  *
+  * 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_controldata.c
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "access/htup_details.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "utils/builtins.h"
+ #include "utils/pg_lsn.h"
+ #include "utils/timestamp.h"
+ 
+ extern ControlData *controldata;
+ 
+ Datum
+ pg_controldata_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[4];
+ 	bool				nulls[4];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(4, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier",
+ 					   INT8OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->catalog_version_no);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int64GetDatum(ControlFile->system_identifier);
+ 	nulls[2] = false;
+ 
+ 	values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
+ 	nulls[3] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_checkpoint_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[19];
+ 	bool				nulls[19];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(19, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* Read the control file. */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	/* Make sure it is valid. */
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/* Populate the values and null arrays */
+ 	values[0] = LSNGetDatum(ControlFile->checkPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
+ 	nulls[2] = false;
+ 
+ 	values[3] = CStringGetTextDatum(xlogfilename);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
+ 	nulls[5] = false;
+ 
+ 	values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
+ 	nulls[6] = false;
+ 
+ 	values[7] = CStringGetTextDatum(psprintf("%u:%u",
+ 								ControlFile->checkPointCopy.nextXidEpoch,
+ 								ControlFile->checkPointCopy.nextXid));
+ 	nulls[7] = false;
+ 
+ 	values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
+ 	nulls[8] = false;
+ 
+ 	values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
+ 	nulls[9] = false;
+ 
+ 	values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
+ 	nulls[10] = false;
+ 
+ 	values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
+ 	nulls[11] = false;
+ 
+ 	values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
+ 	nulls[12] = false;
+ 
+ 	values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
+ 	nulls[13] = false;
+ 
+ 	values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
+ 	nulls[14] = false;
+ 
+ 	values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
+ 	nulls[15] = false;
+ 
+ 	values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	nulls[16] = false;
+ 
+ 	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
+ 	nulls[17] = false;
+ 
+ 	values[18] = TimestampTzGetDatum(
+ 					time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+ 	nulls[18] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_recovery_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[5];
+ 	bool				nulls[5];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(5, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
+ 					   BOOLOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->backupStartPoint);
+ 	nulls[2] = false;
+ 
+ 	values[3] = LSNGetDatum(ControlFile->backupEndPoint);
+ 	nulls[3] = false;
+ 
+ 	values[4] = BoolGetDatum(ControlFile->backupEndRequired);
+ 	nulls[4] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_init_state(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[13];
+ 	bool				nulls[13];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(13, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "bigint_timestamps",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float4_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "float8_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "data_page_checksum_version",
+ 					   INT4OID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->maxAlign);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->blcksz);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int32GetDatum(ControlFile->relseg_size);
+ 	nulls[2] = false;
+ 
+ 	values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->nameDataLen);
+ 	nulls[5] = false;
+ 
+ 	values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
+ 	nulls[6] = false;
+ 
+ 	values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
+ 	nulls[7] = false;
+ 
+ 	values[8] = Int32GetDatum(ControlFile->loblksize);
+ 	nulls[8] = false;
+ 
+ 	values[9] = BoolGetDatum(ControlFile->enableIntTimes);
+ 	nulls[9] = false;
+ 
+ 	values[10] = BoolGetDatum(ControlFile->float4ByVal);
+ 	nulls[10] = false;
+ 
+ 	values[11] = BoolGetDatum(ControlFile->float8ByVal);
+ 	nulls[11] = false;
+ 
+ 	values[12] = Int32GetDatum(ControlFile->data_checksum_version);
+ 	nulls[12] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 5dd2dbc..944a879 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
***************
*** 9,31 ****
   * src/bin/pg_controldata/pg_controldata.c
   */
  
! /*
!  * We have to use postgres.h not postgres_fe.h here, because there's so much
!  * backend-only stuff in the XLOG include files we need.  But we need a
!  * frontend-ish environment otherwise.  Hence this ugly hack.
!  */
! #define FRONTEND 1
! 
! #include "postgres.h"
! 
! #include <unistd.h>
! #include <time.h>
! #include <sys/stat.h>
! #include <fcntl.h>
  
- #include "access/xlog.h"
- #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
  #include "pg_getopt.h"
  
  
--- 9,18 ----
   * src/bin/pg_controldata/pg_controldata.c
   */
  
! #include "postgres_fe.h"
  
  #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
  #include "pg_getopt.h"
  
  
*************** usage(const char *progname)
*** 45,108 ****
  }
  
  
! static const char *
! dbState(DBState state)
! {
! 	switch (state)
! 	{
! 		case DB_STARTUP:
! 			return _("starting up");
! 		case DB_SHUTDOWNED:
! 			return _("shut down");
! 		case DB_SHUTDOWNED_IN_RECOVERY:
! 			return _("shut down in recovery");
! 		case DB_SHUTDOWNING:
! 			return _("shutting down");
! 		case DB_IN_CRASH_RECOVERY:
! 			return _("in crash recovery");
! 		case DB_IN_ARCHIVE_RECOVERY:
! 			return _("in archive recovery");
! 		case DB_IN_PRODUCTION:
! 			return _("in production");
! 	}
! 	return _("unrecognized status code");
! }
! 
! static const char *
! wal_level_str(WalLevel wal_level)
! {
! 	switch (wal_level)
! 	{
! 		case WAL_LEVEL_MINIMAL:
! 			return "minimal";
! 		case WAL_LEVEL_ARCHIVE:
! 			return "archive";
! 		case WAL_LEVEL_HOT_STANDBY:
! 			return "hot_standby";
! 		case WAL_LEVEL_LOGICAL:
! 			return "logical";
! 	}
! 	return _("unrecognized wal_level");
! }
! 
! 
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData ControlFile;
! 	int			fd;
! 	char		ControlFilePath[MAXPGPATH];
! 	char	   *DataDir = NULL;
! 	pg_crc32c	crc;
! 	time_t		time_tmp;
! 	char		pgctime_str[128];
! 	char		ckpttime_str[128];
! 	char		sysident_str[32];
! 	const char *strftime_fmt = "%c";
! 	const char *progname;
! 	XLogSegNo	segno;
! 	char		xlogfilename[MAXFNAMELEN];
! 	int			c;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata"));
  
--- 32,48 ----
  }
  
  
! #define CONTROLDATANAME_LEN 39
! #define CONTROLDATANAME_FMT "%-39s%s\n"
  int
  main(int argc, char *argv[])
  {
! 	char		   *DataDir = NULL;
! 	ControlData	   *controldata;
! 	size_t			controldata_len;
! 	char			controldata_name[CONTROLDATANAME_LEN + 1];
! 	const char	   *progname;
! 	int				c, i;
  
  	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_controldata"));
  
*************** main(int argc, char *argv[])
*** 161,338 ****
  		exit(1);
  	}
  
! 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
! 
! 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
! 	{
! 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 
! 	if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
  	{
! 		fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
  	}
- 	close(fd);
- 
- 	/* Check the CRC. */
- 	INIT_CRC32C(crc);
- 	COMP_CRC32C(crc,
- 				(char *) &ControlFile,
- 				offsetof(ControlFileData, crc));
- 	FIN_CRC32C(crc);
- 
- 	if (!EQ_CRC32C(crc, ControlFile.crc))
- 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
- 				 "Either the file is corrupt, or it has a different layout than this program\n"
- 				 "is expecting.  The results below are untrustworthy.\n\n"));
- 
- 	/*
- 	 * This slightly-chintzy coding will work as long as the control file
- 	 * timestamps are within the range of time_t; that should be the case in
- 	 * all foreseeable circumstances, so we don't bother importing the
- 	 * backend's timezone library into pg_controldata.
- 	 *
- 	 * Use variable for format to suppress overly-anal-retentive gcc warning
- 	 * about %c
- 	 */
- 	time_tmp = (time_t) ControlFile.time;
- 	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
- 			 localtime(&time_tmp));
- 	time_tmp = (time_t) ControlFile.checkPointCopy.time;
- 	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
- 			 localtime(&time_tmp));
  
- 	/*
- 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
- 	 * start point.
- 	 */
- 	XLByteToSeg(ControlFile.checkPointCopy.redo, segno);
- 	XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno);
- 
- 	/*
- 	 * Format system_identifier separately to keep platform-dependent format
- 	 * code out of the translatable message string.
- 	 */
- 	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
- 			 ControlFile.system_identifier);
- 
- 	printf(_("pg_control version number:            %u\n"),
- 		   ControlFile.pg_control_version);
- 	if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0)
- 		printf(_("WARNING: possible byte ordering mismatch\n"
- 				 "The byte ordering used to store the pg_control file might not match the one\n"
- 				 "used by this program.  In that case the results below would be incorrect, and\n"
- 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
- 	printf(_("Catalog version number:               %u\n"),
- 		   ControlFile.catalog_version_no);
- 	printf(_("Database system identifier:           %s\n"),
- 		   sysident_str);
- 	printf(_("Database cluster state:               %s\n"),
- 		   dbState(ControlFile.state));
- 	printf(_("pg_control last modified:             %s\n"),
- 		   pgctime_str);
- 	printf(_("Latest checkpoint location:           %X/%X\n"),
- 		   (uint32) (ControlFile.checkPoint >> 32),
- 		   (uint32) ControlFile.checkPoint);
- 	printf(_("Prior checkpoint location:            %X/%X\n"),
- 		   (uint32) (ControlFile.prevCheckPoint >> 32),
- 		   (uint32) ControlFile.prevCheckPoint);
- 	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
- 		   (uint32) (ControlFile.checkPointCopy.redo >> 32),
- 		   (uint32) ControlFile.checkPointCopy.redo);
- 	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
- 		   xlogfilename);
- 	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
- 		   ControlFile.checkPointCopy.ThisTimeLineID);
- 	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
- 		   ControlFile.checkPointCopy.PrevTimeLineID);
- 	printf(_("Latest checkpoint's full_page_writes: %s\n"),
- 		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
- 	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
- 		   ControlFile.checkPointCopy.nextXidEpoch,
- 		   ControlFile.checkPointCopy.nextXid);
- 	printf(_("Latest checkpoint's NextOID:          %u\n"),
- 		   ControlFile.checkPointCopy.nextOid);
- 	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
- 		   ControlFile.checkPointCopy.nextMulti);
- 	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
- 		   ControlFile.checkPointCopy.nextMultiOffset);
- 	printf(_("Latest checkpoint's oldestXID:        %u\n"),
- 		   ControlFile.checkPointCopy.oldestXid);
- 	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
- 		   ControlFile.checkPointCopy.oldestXidDB);
- 	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
- 		   ControlFile.checkPointCopy.oldestActiveXid);
- 	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
- 		   ControlFile.checkPointCopy.oldestMulti);
- 	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
- 		   ControlFile.checkPointCopy.oldestMultiDB);
- 	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
- 		   ControlFile.checkPointCopy.oldestCommitTsXid);
- 	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
- 		   ControlFile.checkPointCopy.newestCommitTsXid);
- 	printf(_("Time of latest checkpoint:            %s\n"),
- 		   ckpttime_str);
- 	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
- 		   (uint32) (ControlFile.unloggedLSN >> 32),
- 		   (uint32) ControlFile.unloggedLSN);
- 	printf(_("Minimum recovery ending location:     %X/%X\n"),
- 		   (uint32) (ControlFile.minRecoveryPoint >> 32),
- 		   (uint32) ControlFile.minRecoveryPoint);
- 	printf(_("Min recovery ending loc's timeline:   %u\n"),
- 		   ControlFile.minRecoveryPointTLI);
- 	printf(_("Backup start location:                %X/%X\n"),
- 		   (uint32) (ControlFile.backupStartPoint >> 32),
- 		   (uint32) ControlFile.backupStartPoint);
- 	printf(_("Backup end location:                  %X/%X\n"),
- 		   (uint32) (ControlFile.backupEndPoint >> 32),
- 		   (uint32) ControlFile.backupEndPoint);
- 	printf(_("End-of-backup record required:        %s\n"),
- 		   ControlFile.backupEndRequired ? _("yes") : _("no"));
- 	printf(_("wal_level setting:                    %s\n"),
- 		   wal_level_str(ControlFile.wal_level));
- 	printf(_("wal_log_hints setting:                %s\n"),
- 		   ControlFile.wal_log_hints ? _("on") : _("off"));
- 	printf(_("max_connections setting:              %d\n"),
- 		   ControlFile.MaxConnections);
- 	printf(_("max_worker_processes setting:         %d\n"),
- 		   ControlFile.max_worker_processes);
- 	printf(_("max_prepared_xacts setting:           %d\n"),
- 		   ControlFile.max_prepared_xacts);
- 	printf(_("max_locks_per_xact setting:           %d\n"),
- 		   ControlFile.max_locks_per_xact);
- 	printf(_("track_commit_timestamp setting:       %s\n"),
- 		   ControlFile.track_commit_timestamp ? _("on") : _("off"));
- 	printf(_("Maximum data alignment:               %u\n"),
- 		   ControlFile.maxAlign);
- 	/* we don't print floatFormat since can't say much useful about it */
- 	printf(_("Database block size:                  %u\n"),
- 		   ControlFile.blcksz);
- 	printf(_("Blocks per segment of large relation: %u\n"),
- 		   ControlFile.relseg_size);
- 	printf(_("WAL block size:                       %u\n"),
- 		   ControlFile.xlog_blcksz);
- 	printf(_("Bytes per WAL segment:                %u\n"),
- 		   ControlFile.xlog_seg_size);
- 	printf(_("Maximum length of identifiers:        %u\n"),
- 		   ControlFile.nameDataLen);
- 	printf(_("Maximum columns in an index:          %u\n"),
- 		   ControlFile.indexMaxKeys);
- 	printf(_("Maximum size of a TOAST chunk:        %u\n"),
- 		   ControlFile.toast_max_chunk_size);
- 	printf(_("Size of a large-object chunk:         %u\n"),
- 		   ControlFile.loblksize);
- 	printf(_("Date/time type storage:               %s\n"),
- 		   (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
- 	printf(_("Float4 argument passing:              %s\n"),
- 		   (ControlFile.float4ByVal ? _("by value") : _("by reference")));
- 	printf(_("Float8 argument passing:              %s\n"),
- 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
- 	printf(_("Data page checksum version:           %u\n"),
- 		   ControlFile.data_checksum_version);
  	return 0;
  }
--- 101,114 ----
  		exit(1);
  	}
  
! 	controldata = get_controldata(DataDir, progname, &controldata_len);
! 	for (i = 0; i < controldata_len; i++)
  	{
! 		memset(controldata_name, ' ', CONTROLDATANAME_LEN);
! 		snprintf (controldata_name, CONTROLDATANAME_LEN,
! 				  "%s:", controldata[i].name);
! 		printf(CONTROLDATANAME_FMT, controldata_name, controldata[i].setting);
  	}
  
  	return 0;
  }
diff --git a/src/common/Makefile b/src/common/Makefile
index bde4fc2..f7a4a4d 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** override CPPFLAGS += -DVAL_LDFLAGS_EX="\
*** 36,43 ****
  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
  
--- 36,43 ----
  override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
  override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
  
! OBJS_COMMON = config_info.o controldata_utils.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
  
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index ...686be76 .
*** a/src/common/controldata_utils.c
--- b/src/common/controldata_utils.c
***************
*** 0 ****
--- 1,382 ----
+ /*-------------------------------------------------------------------------
+  *
+  * controldata_utils.c
+  *		Common code for pg_controldata output
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff in the XLOG include files we need.
+  */
+ #include "postgres.h"
+ 
+ #include <unistd.h>
+ #include <time.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ 
+ #include "miscadmin.h"
+ #include "access/xlog.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "port/pg_crc32c.h"
+ #include "utils/builtins.h"
+ 
+ #ifndef FRONTEND
+ /* NOTE: caller must provide gettext call around str */
+ #define log_error(str, param1, param2)	\
+ 	elog(ERROR, str, param1, param2)
+ #else
+ #define log_error(str, param1, param2)	\
+ 	do { \
+ 			char *buf = psprintf("%%s: %s", str); \
+ 			fprintf(stderr, buf, progname, param1, param2); \
+ 			exit(2); \
+ 	} while (0)
+ #endif
+ 
+ static const char *dbState(DBState state);
+ static const char *wal_level_str(WalLevel wal_level);
+ 
+ static const char *const controldata_names[] =
+ {
+ 	gettext_noop("pg_control version number"),
+ 	gettext_noop("Catalog version number"),
+ 	gettext_noop("Database system identifier"),
+ 	gettext_noop("Database cluster state"),
+ 	gettext_noop("pg_control last modified"),
+ 	gettext_noop("Latest checkpoint location"),
+ 	gettext_noop("Prior checkpoint location"),
+ 	gettext_noop("Latest checkpoint's REDO location"),
+ 	gettext_noop("Latest checkpoint's REDO WAL file"),
+ 	gettext_noop("Latest checkpoint's TimeLineID"),
+ 	gettext_noop("Latest checkpoint's PrevTimeLineID"),
+ 	gettext_noop("Latest checkpoint's full_page_writes"),
+ 	gettext_noop("Latest checkpoint's NextXID"),
+ 	gettext_noop("Latest checkpoint's NextOID"),
+ 	gettext_noop("Latest checkpoint's NextMultiXactId"),
+ 	gettext_noop("Latest checkpoint's NextMultiOffset"),
+ 	gettext_noop("Latest checkpoint's oldestXID"),
+ 	gettext_noop("Latest checkpoint's oldestXID's DB"),
+ 	gettext_noop("Latest checkpoint's oldestActiveXID"),
+ 	gettext_noop("Latest checkpoint's oldestMultiXid"),
+ 	gettext_noop("Latest checkpoint's oldestMulti's DB"),
+ 	gettext_noop("Latest checkpoint's oldestCommitTsXid"),
+ 	gettext_noop("Latest checkpoint's newestCommitTsXid"),
+ 	gettext_noop("Time of latest checkpoint"),
+ 	gettext_noop("Fake LSN counter for unlogged rels"),
+ 	gettext_noop("Minimum recovery ending location"),
+ 	gettext_noop("Min recovery ending loc's timeline"),
+ 	gettext_noop("Backup start location"),
+ 	gettext_noop("Backup end location"),
+ 	gettext_noop("End-of-backup record required"),
+ 	gettext_noop("wal_level setting"),
+ 	gettext_noop("wal_log_hints setting"),
+ 	gettext_noop("max_connections setting"),
+ 	gettext_noop("max_worker_processes setting"),
+ 	gettext_noop("max_prepared_xacts setting"),
+ 	gettext_noop("max_locks_per_xact setting"),
+ 	gettext_noop("track_commit_timestamp setting"),
+ 	gettext_noop("Maximum data alignment"),
+ 	gettext_noop("Database block size"),
+ 	gettext_noop("Blocks per segment of large relation"),
+ 	gettext_noop("WAL block size"),
+ 	gettext_noop("Bytes per WAL segment"),
+ 	gettext_noop("Maximum length of identifiers"),
+ 	gettext_noop("Maximum columns in an index"),
+ 	gettext_noop("Maximum size of a TOAST chunk"),
+ 	gettext_noop("Size of a large-object chunk"),
+ 	gettext_noop("Date/time type storage"),
+ 	gettext_noop("Float4 argument passing"),
+ 	gettext_noop("Float8 argument passing"),
+ 	gettext_noop("Data page checksum version"),
+ 	NULL
+ };
+ 
+ static size_t
+ controldata_names_len(void)
+ {
+ 	size_t	i = 0;
+ 
+ 	while (controldata_names[i])
+ 		i++;
+ 
+ 	return i;
+ }
+ 
+ 
+ static const char *
+ dbState(DBState state)
+ {
+ 	switch (state)
+ 	{
+ 		case DB_STARTUP:
+ 			return _("starting up");
+ 		case DB_SHUTDOWNED:
+ 			return _("shut down");
+ 		case DB_SHUTDOWNED_IN_RECOVERY:
+ 			return _("shut down in recovery");
+ 		case DB_SHUTDOWNING:
+ 			return _("shutting down");
+ 		case DB_IN_CRASH_RECOVERY:
+ 			return _("in crash recovery");
+ 		case DB_IN_ARCHIVE_RECOVERY:
+ 			return _("in archive recovery");
+ 		case DB_IN_PRODUCTION:
+ 			return _("in production");
+ 	}
+ 	return _("unrecognized status code");
+ }
+ 
+ static const char *
+ wal_level_str(WalLevel wal_level)
+ {
+ 	switch (wal_level)
+ 	{
+ 		case WAL_LEVEL_MINIMAL:
+ 			return "minimal";
+ 		case WAL_LEVEL_ARCHIVE:
+ 			return "archive";
+ 		case WAL_LEVEL_HOT_STANDBY:
+ 			return "hot_standby";
+ 		case WAL_LEVEL_LOGICAL:
+ 			return "logical";
+ 	}
+ 	return _("unrecognized wal_level");
+ }
+ 
+ 
+ ControlFileData *
+ get_controlfile(char *DataDir, const char *progname)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	int					fd;
+ 	char				ControlFilePath[MAXPGPATH];
+ 	pg_crc32c			crc;
+ 
+ 	ControlFile = palloc(sizeof(ControlFileData));
+ 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+ 
+ 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+ 		log_error(_("could not open file \"%s\" for reading: %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+ 		log_error(_("could not read file \"%s\": %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	close(fd);
+ 
+ 	/* Check the CRC. */
+ 	INIT_CRC32C(crc);
+ 	COMP_CRC32C(crc,
+ 			   (char *) ControlFile,
+ 			   offsetof(ControlFileData, crc));
+ 	FIN_CRC32C(crc);
+ 
+ 	if (!EQ_CRC32C(crc, ControlFile->crc))
+ #ifndef FRONTEND
+ 		elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
+ #else
+ 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+ 				 "Either the file is corrupt, or it has a different layout than this program\n"
+ 				 "is expecting.  The results below are untrustworthy.\n\n"));
+ #endif
+ 
+ 	return ControlFile;
+ }
+ 
+ ControlData *
+ get_controldata(char *DataDir, const char *progname,
+ 				size_t *controldata_len)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	ControlData		   *controldata;
+ 	time_t				time_tmp;
+ 	char				pgctime_str[128];
+ 	char				ckpttime_str[128];
+ 	char				sysident_str[32];
+ 	const char		   *strftime_fmt = "%c";
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 	int					i;
+ 	int					idx = 0;
+ 
+ 	*controldata_len = controldata_names_len();
+ 	controldata = palloc(*controldata_len * sizeof(ControlData));
+ 
+ 	/*
+ 	 * initialize controldata names
+ 	 *
+ 	 * These better be in sync with the settings manually
+ 	 * defined below.
+ 	 */
+ 	for (i = 0; i < *controldata_len; i++)
+ 		controldata[i].name = pstrdup(_(controldata_names[i]));
+ 
+ 	/* get a copy of the control file */
+ 	ControlFile = get_controlfile(DataDir, progname);
+ 
+ 	/*
+ 	 * This slightly-chintzy coding will work as long as the control file
+ 	 * timestamps are within the range of time_t; that should be the case in
+ 	 * all foreseeable circumstances, so we don't bother importing the
+ 	 * backend's timezone library.
+ 	 *
+ 	 * Use variable for format to suppress overly-anal-retentive gcc warning
+ 	 * about %c
+ 	 */
+ 	time_tmp = (time_t) ControlFile->time;
+ 	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 	time_tmp = (time_t) ControlFile->checkPointCopy.time;
+ 	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
+ 			 localtime(&time_tmp));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/*
+ 	 * Format system_identifier separately to keep platform-dependent format
+ 	 * code out of the translatable message string.
+ 	 */
+ 	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
+ 			 ControlFile->system_identifier);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ #ifndef FRONTEND
+ 		elog(ERROR, _("byte ordering mismatch"));
+ #else
+ 		printf(_("WARNING: possible byte ordering mismatch\n"
+ 				 "The byte ordering used to store the pg_control file might not match the one\n"
+ 				 "used by this program.  In that case the results below would be incorrect, and\n"
+ 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
+ #endif
+ 
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->pg_control_version);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->catalog_version_no);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		sysident_str);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		dbState(ControlFile->state));
+ 	controldata[idx++].setting = psprintf("%s",
+ 		pgctime_str);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->checkPoint >> 32),
+ 		(uint32) ControlFile->checkPoint);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->prevCheckPoint >> 32),
+ 		(uint32) ControlFile->prevCheckPoint);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->checkPointCopy.redo >> 32),
+ 		(uint32) ControlFile->checkPointCopy.redo);
+ 	controldata[idx++].setting = psprintf("%s", xlogfilename);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.ThisTimeLineID);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.PrevTimeLineID);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
+ 	controldata[idx++].setting = psprintf("%u:%u",
+ 		ControlFile->checkPointCopy.nextXidEpoch,
+ 		ControlFile->checkPointCopy.nextXid);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextOid);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextMulti);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.nextMultiOffset);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestXid);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestXidDB);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestActiveXid);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestMulti);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestMultiDB);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->checkPointCopy.newestCommitTsXid);
+ 	controldata[idx++].setting = psprintf("%s", ckpttime_str);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->unloggedLSN >> 32),
+ 		(uint32) ControlFile->unloggedLSN);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->minRecoveryPoint >> 32),
+ 		(uint32) ControlFile->minRecoveryPoint);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->minRecoveryPointTLI);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->backupStartPoint >> 32),
+ 		(uint32) ControlFile->backupStartPoint);
+ 	controldata[idx++].setting = psprintf("%X/%X",
+ 		(uint32) (ControlFile->backupEndPoint >> 32),
+ 		(uint32) ControlFile->backupEndPoint);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		ControlFile->backupEndRequired ? _("yes") : _("no"));
+ 	controldata[idx++].setting = psprintf("%s",
+ 		wal_level_str(ControlFile->wal_level));
+ 	controldata[idx++].setting = psprintf("%s",
+ 		ControlFile->wal_log_hints ? _("on") : _("off"));
+ 	controldata[idx++].setting = psprintf("%d",
+ 		ControlFile->MaxConnections);
+ 	controldata[idx++].setting = psprintf("%d",
+ 		ControlFile->max_worker_processes);
+ 	controldata[idx++].setting = psprintf("%d",
+ 		ControlFile->max_prepared_xacts);
+ 	controldata[idx++].setting = psprintf("%d",
+ 		ControlFile->max_locks_per_xact);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		ControlFile->track_commit_timestamp ? _("on") : _("off"));
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->maxAlign);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->blcksz);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->relseg_size);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->xlog_blcksz);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->xlog_seg_size);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->nameDataLen);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->indexMaxKeys);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->toast_max_chunk_size);
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->loblksize);
+ 	controldata[idx++].setting = psprintf("%s",
+ 		(ControlFile->enableIntTimes ?
+ 		 _("64-bit integers") : _("floating-point numbers")));
+ 	controldata[idx++].setting = psprintf("%s",
+ 		(ControlFile->float4ByVal ? _("by value") : _("by reference")));
+ 	controldata[idx++].setting = psprintf("%s",
+ 		(ControlFile->float8ByVal ? _("by value") : _("by reference")));
+ 	controldata[idx++].setting = psprintf("%u",
+ 		ControlFile->data_checksum_version);
+ 
+ 	return controldata;
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 2222e8f..2e269aa 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("row security for current context
*** 5212,5217 ****
--- 5212,5230 ----
  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");
  
+ /* pg_controldata related functions */
+ DATA(insert OID = 3441 ( pg_controldata_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,20,1184}" "{o,o,o,o}" "{pg_control_version,catalog_version_no,system_identifier,pg_control_last_modified}" _null_ _null_ pg_controldata_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata general state information as a function");
+ 
+ DATA(insert OID = 3442 ( pg_checkpoint_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,3220,3220,25,23,23,16,25,26,28,28,28,26,28,28,26,28,28,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{checkpoint_location,prior_location,redo_location,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}" _null_ _null_ pg_checkpoint_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata checkpoint state information as a function");
+ 
+ DATA(insert OID = 3443 ( pg_recovery_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,23,3220,3220,16}" "{o,o,o,o,o}" "{min_recovery_end_location,min_recovery_end_timeline,backup_start_location,backup_end_location,end_of_backup_record_required}" _null_ _null_ pg_recovery_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata recovery state information as a function");
+ 
+ DATA(insert OID = 3444 ( pg_init_state PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_init_state _null_ _null_ _null_ ));
+ DESCR("pg_controldata init state information 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/controldata_utils.h b/src/include/common/controldata_utils.h
index ...5dcc45f .
*** a/src/include/common/controldata_utils.h
--- b/src/include/common/controldata_utils.h
***************
*** 0 ****
--- 1,23 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  *	Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *	src/include/common/controldata_utils.h
+  */
+ #ifndef COMMON_CONTROLDATA_UTILS_H
+ #define COMMON_CONTROLDATA_UTILS_H
+ 
+ typedef struct ControlData
+ {
+ 	char	   *name;
+ 	char	   *setting;
+ } ControlData;
+ 
+ extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+ extern ControlData *get_controldata(char *DataDir, const char *progname,
+ 									size_t *controldata_len);
+ 
+ #endif   /* COMMON_CONTROLDATA_UTILS_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index a784de9..6fbd89c 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum show_all_file_settings(PG_F
*** 1150,1155 ****
--- 1150,1161 ----
  /* pg_config.c */
  extern Datum pg_config(PG_FUNCTION_ARGS);
  
+ /* pg_controldata.c */
+ extern Datum pg_checkpoint_state(PG_FUNCTION_ARGS);
+ extern Datum pg_controldata_state(PG_FUNCTION_ARGS);
+ extern Datum pg_init_state(PG_FUNCTION_ARGS);
+ extern Datum pg_recovery_state(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/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4fb44e..1a9c996 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,112 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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 = (
--- 106,112 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  config_info.c controldata_utils.c exec.c pg_lzcompress.c pgfnames.c
  	  relpath.c rmtree.c string.c username.c wait_error.c);
  
  	our @pgcommonfrontendfiles = (
#101Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#100)
Re: exposing pg_controldata and pg_config as functions

On Sat, Feb 20, 2016 at 12:12 PM, Joe Conway <mail@joeconway.com> wrote:

On 01/17/2016 04:10 PM, Joe Conway wrote:

On 01/16/2016 06:02 AM, Michael Paquier wrote:

On Wed, Dec 30, 2015 at 9:08 AM, Joe Conway <mail@joeconway.com> wrote:

3) Adds new functions, more or less in line with previous discussions:
* pg_checkpoint_state()
* pg_controldata_state()
* pg_recovery_state()
* pg_init_state()

Taking the opposite direction of Josh upthread, why is this split
actually necessary? Isn't the idea to provide a SQL interface of what
pg_controldata shows? If this split proves to be useful, shouldn't we
do it as well for pg_controldata?

The last discussion moved strongly in the direction of separate
functions, and that being different from pg_controldata was not a bad
thing. That said, I'm still of the opinion that there are legitimate
reasons to want the command line pg_controldata and the SQL functions to
produce equivalent, if not identical, results. I just wish we could get
a clear consensus one way or the other.

I've assumed that we are sticking with the separate functions. As such,
here is a rebased patch, with documentation and other fixes such as
Copyright year, Mkvcbuild support, and some cruft removal.

Looking again at this thread I guess that this is consensus, based on
the proposal from Josh and seeing no other ideas around. Another idea
would be to group all the fields that into a single function
pg_control_data().

Is there general consensus that we want this feature, and that we want
it in this form? Any other comments?

I had a look at this patch.

+   <indexterm>
+    <primary>pg_checkpoint_state</primary>
+   </indexterm>
+   <para>
+    <function>pg_checkpoint_state</> returns a record containing
+    checkpoint_location, prior_location, redo_location, redo_wal_file,
+    timeline_id, prev_timeline_id, full_page_writes, next_xid, next_oid,
+    next_multixact_id, next_multi_offset, oldest_xid, oldest_xid_dbid,
+    oldest_active_xid, oldest_multi_xid, oldest_multi_dbid,
+    oldest_commit_ts_xid, newest_commit_ts_xid, and checkpoint_time.
+   </para>
This is bit unreadable. The only entry in the documentation that
adopts a similar style is pg_stat_file, and with six fields that feels
as being enough. I would suggest using a table instead with the type
of the field and its name.

Regarding the naming of the functions, I think that it would be good
to get something consistent with the concept of those being "Control
Data functions" by having them share the same prefix, say pg_control_
- pg_control_checkpoint
- pg_control_init
- pg_control_system
- pg_control_recovery

+       snprintf (controldata_name, CONTROLDATANAME_LEN,
+                 "%s:", controldata[i].name);
Nitpick: extra space.
+static const char *const controldata_names[] =
+{
+   gettext_noop("pg_control version number"),
+   gettext_noop("Catalog version number"),
+   gettext_noop("Database system identifier"),
Is this complication really necessary? Those identifiers are used only
in the frontend and the footprint of this patch on pg_controldata is
really large. What I think we should do is have in src/common the
following set of routines that work directly on ControlFileData:
- checkControlFile, to perform basic sanity checks on the control file
(CRC, see for example pg_rewind.c)
- getControlFile(dataDir), that simply returns a palloc'd
ControlFileData to the caller after looking at global/pg_control.
pg_rewind could perhaps make use of the one to check the control file
CRC, to fetch ControlFileData there is some parallel logic for the
source server if it is either remote or local so it would be better to
not use getControlFile in this case.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#102Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#101)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 02/21/2016 05:30 AM, Michael Paquier wrote:

Looking again at this thread I guess that this is consensus, based on
the proposal from Josh and seeing no other ideas around. Another idea
would be to group all the fields that into a single function
pg_control_data().

I think a single function would be ridiculously wide. I like the four
separate functions better if we're going to do it this way at all.

+   <indexterm>
+    <primary>pg_checkpoint_state</primary>
+   </indexterm>
+   <para>
+    <function>pg_checkpoint_state</> returns a record containing
+    checkpoint_location, prior_location, redo_location, redo_wal_file,
+    timeline_id, prev_timeline_id, full_page_writes, next_xid, next_oid,
+    next_multixact_id, next_multi_offset, oldest_xid, oldest_xid_dbid,
+    oldest_active_xid, oldest_multi_xid, oldest_multi_dbid,
+    oldest_commit_ts_xid, newest_commit_ts_xid, and checkpoint_time.
+   </para>
This is bit unreadable. The only entry in the documentation that
adopts a similar style is pg_stat_file, and with six fields that feels
as being enough. I would suggest using a table instead with the type
of the field and its name.

Ok, changed to your suggestion.

Regarding the naming of the functions, I think that it would be good
to get something consistent with the concept of those being "Control
Data functions" by having them share the same prefix, say pg_control_
- pg_control_checkpoint
- pg_control_init
- pg_control_system
- pg_control_recovery

No issues -- changed.

+       snprintf (controldata_name, CONTROLDATANAME_LEN,
+                 "%s:", controldata[i].name);
Nitpick: extra space.

I didn't understand this comment but it is moot now anyway...

+static const char *const controldata_names[] =
+{
+   gettext_noop("pg_control version number"),
+   gettext_noop("Catalog version number"),
+   gettext_noop("Database system identifier"),
Is this complication really necessary? Those identifiers are used only
in the frontend and the footprint of this patch on pg_controldata is
really large. What I think we should do is have in src/common the
following set of routines that work directly on ControlFileData:
- checkControlFile, to perform basic sanity checks on the control file
(CRC, see for example pg_rewind.c)
- getControlFile(dataDir), that simply returns a palloc'd
ControlFileData to the caller after looking at global/pg_control.
pg_rewind could perhaps make use of the one to check the control file
CRC, to fetch ControlFileData there is some parallel logic for the
source server if it is either remote or local so it would be better to
not use getControlFile in this case.

I agree with the assessment that much of what had been moved based on
the original pg_controladata() SRF no longer needs to move. This version
only puts get_controlfile() into src/common, since that is the bit that
is still shared. If checkControlFile() or something similar is useful
for pg_rewind or some other extension, I'd say that should be a separate
patch.

Oh, and the entire thing is now rebased against a git pull from a few
hours ago. I moved this to the upcoming commitfest too, although I think
it is pretty well ready to go.

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_controldata_funcs-2016.02.27.02.difftext/x-diff; name=pg_controldata_funcs-2016.02.27.02.diffDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c0b94bc..4b5ee81 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT collation for ('foo' COLLATE "de_
*** 16703,16708 ****
--- 16703,17064 ----
      </tgroup>
     </table>
  
+    <para>
+     The functions shown in <xref linkend="functions-controldata">
+     print information initialized during <command>initdb</>, such
+     as the catalog version. They also show information about write-ahead
+     logging and checkpoint processing. This information is cluster-wide,
+     and not specific to any one database. They provide most of the same
+     information, from the same source, as
+     <xref linkend="APP-PGCONTROLDATA">, although in a form better suited
+     to <acronym>SQL</acronym> functions.
+    </para>
+ 
+    <table id="functions-controldata">
+     <title>Control Data Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_checkpoint</primary></indexterm>
+         <literal><function>pg_control_checkpoint()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current checkpoint state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_system</primary></indexterm>
+         <literal><function>pg_control_system()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current controldata file state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_init</primary></indexterm>
+         <literal><function>pg_control_init()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about cluster initialization state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_recovery</primary></indexterm>
+         <literal><function>pg_control_recovery()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about recovery state.
+        </entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_checkpoint</> returns a record, shown in
+     <xref linkend="functions-pg-control-checkpoint">
+    </para>
+ 
+    <table id="functions-pg-control-checkpoint">
+     <title><function>pg_control_checkpoint</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>checkpoint_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>prior_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>redo_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>redo_wal_file</entry>
+        <entry><type>text</type></entry>
+       </row>
+ 
+       <row>
+        <entry>timeline_id</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>prev_timeline_id</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>full_page_writes</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_xid</entry>
+        <entry><type>text</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_oid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_multixact_id</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_multi_offset</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_xid_dbid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_active_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_multi_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_multi_dbid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_commit_ts_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>newest_commit_ts_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>checkpoint_time</entry>
+        <entry><type>timestamp with time zone</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_system</> returns a record, shown in
+     <xref linkend="functions-pg-control-system">
+    </para>
+ 
+    <table id="functions-pg-control-system">
+     <title><function>pg_control_system</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>pg_control_version</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>catalog_version_no</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>system_identifier</entry>
+        <entry><type>bigint</type></entry>
+       </row>
+ 
+       <row>
+        <entry>pg_control_last_modified</entry>
+        <entry><type>timestamp with time zone</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_init</> returns a record, shown in
+     <xref linkend="functions-pg-control-init">
+    </para>
+ 
+    <table id="functions-pg-control-init">
+     <title><function>pg_control_init</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>max_data_alignment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>database_block_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>blocks_per_segment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>wal_block_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>bytes_per_wal_segment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_identifier_length</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_index_columns</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_toast_chunk_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>large_object_chunk_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>bigint_timestamps</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>float4_pass_by_value</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>float8_pass_by_value</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>data_page_checksum_version</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_recovery</> returns a record, shown in
+     <xref linkend="functions-pg-control-recovery">
+    </para>
+ 
+    <table id="functions-pg-control-recovery">
+     <title><function>pg_control_recovery</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>min_recovery_end_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>min_recovery_end_timeline</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>backup_start_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>backup_end_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>end_of_backup_record_required</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
    </sect1>
  
    <sect1 id="functions-admin">
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index a0c82c1..a5b487d 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,20 ****
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
--- 14,20 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! OBJS = guc.o help_config.o pg_config.o pg_controldata.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
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index ...6aa4fdf .
*** a/src/backend/utils/misc/pg_controldata.c
--- b/src/backend/utils/misc/pg_controldata.c
***************
*** 0 ****
--- 1,356 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_controldata.c
+  *		Expose select pg_controldata output, except via SQL functions
+  *
+  * 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_controldata.c
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "access/htup_details.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "utils/builtins.h"
+ #include "utils/pg_lsn.h"
+ #include "utils/timestamp.h"
+ 
+ Datum
+ pg_control_system(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[4];
+ 	bool				nulls[4];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(4, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier",
+ 					   INT8OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->catalog_version_no);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int64GetDatum(ControlFile->system_identifier);
+ 	nulls[2] = false;
+ 
+ 	values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
+ 	nulls[3] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_checkpoint(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[19];
+ 	bool				nulls[19];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(19, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* Read the control file. */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	/* Make sure it is valid. */
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/* Populate the values and null arrays */
+ 	values[0] = LSNGetDatum(ControlFile->checkPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
+ 	nulls[2] = false;
+ 
+ 	values[3] = CStringGetTextDatum(xlogfilename);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
+ 	nulls[5] = false;
+ 
+ 	values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
+ 	nulls[6] = false;
+ 
+ 	values[7] = CStringGetTextDatum(psprintf("%u:%u",
+ 								ControlFile->checkPointCopy.nextXidEpoch,
+ 								ControlFile->checkPointCopy.nextXid));
+ 	nulls[7] = false;
+ 
+ 	values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
+ 	nulls[8] = false;
+ 
+ 	values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
+ 	nulls[9] = false;
+ 
+ 	values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
+ 	nulls[10] = false;
+ 
+ 	values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
+ 	nulls[11] = false;
+ 
+ 	values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
+ 	nulls[12] = false;
+ 
+ 	values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
+ 	nulls[13] = false;
+ 
+ 	values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
+ 	nulls[14] = false;
+ 
+ 	values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
+ 	nulls[15] = false;
+ 
+ 	values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	nulls[16] = false;
+ 
+ 	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
+ 	nulls[17] = false;
+ 
+ 	values[18] = TimestampTzGetDatum(
+ 					time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+ 	nulls[18] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_recovery(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[5];
+ 	bool				nulls[5];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(5, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
+ 					   BOOLOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->backupStartPoint);
+ 	nulls[2] = false;
+ 
+ 	values[3] = LSNGetDatum(ControlFile->backupEndPoint);
+ 	nulls[3] = false;
+ 
+ 	values[4] = BoolGetDatum(ControlFile->backupEndRequired);
+ 	nulls[4] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_init(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[13];
+ 	bool				nulls[13];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(13, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "bigint_timestamps",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float4_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "float8_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "data_page_checksum_version",
+ 					   INT4OID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ 		elog(ERROR, _("byte ordering mismatch"));
+ 
+ 	values[0] = Int32GetDatum(ControlFile->maxAlign);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->blcksz);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int32GetDatum(ControlFile->relseg_size);
+ 	nulls[2] = false;
+ 
+ 	values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->nameDataLen);
+ 	nulls[5] = false;
+ 
+ 	values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
+ 	nulls[6] = false;
+ 
+ 	values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
+ 	nulls[7] = false;
+ 
+ 	values[8] = Int32GetDatum(ControlFile->loblksize);
+ 	nulls[8] = false;
+ 
+ 	values[9] = BoolGetDatum(ControlFile->enableIntTimes);
+ 	nulls[9] = false;
+ 
+ 	values[10] = BoolGetDatum(ControlFile->float4ByVal);
+ 	nulls[10] = false;
+ 
+ 	values[11] = BoolGetDatum(ControlFile->float8ByVal);
+ 	nulls[11] = false;
+ 
+ 	values[12] = Int32GetDatum(ControlFile->data_checksum_version);
+ 	nulls[12] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 5dd2dbc..756b641 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
***************
*** 18,31 ****
  
  #include "postgres.h"
  
- #include <unistd.h>
  #include <time.h>
- #include <sys/stat.h>
- #include <fcntl.h>
  
  #include "access/xlog.h"
  #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
  #include "pg_getopt.h"
  
  
--- 18,29 ----
  
  #include "postgres.h"
  
  #include <time.h>
  
  #include "access/xlog.h"
  #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
  #include "pg_getopt.h"
  
  
*************** wal_level_str(WalLevel wal_level)
*** 89,99 ****
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData ControlFile;
! 	int			fd;
! 	char		ControlFilePath[MAXPGPATH];
  	char	   *DataDir = NULL;
- 	pg_crc32c	crc;
  	time_t		time_tmp;
  	char		pgctime_str[128];
  	char		ckpttime_str[128];
--- 87,94 ----
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData *ControlFile;
  	char	   *DataDir = NULL;
  	time_t		time_tmp;
  	char		pgctime_str[128];
  	char		ckpttime_str[128];
*************** main(int argc, char *argv[])
*** 161,194 ****
  		exit(1);
  	}
  
! 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
! 
! 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
! 	{
! 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 
! 	if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
! 	{
! 		fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 	close(fd);
! 
! 	/* Check the CRC. */
! 	INIT_CRC32C(crc);
! 	COMP_CRC32C(crc,
! 				(char *) &ControlFile,
! 				offsetof(ControlFileData, crc));
! 	FIN_CRC32C(crc);
! 
! 	if (!EQ_CRC32C(crc, ControlFile.crc))
! 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
! 				 "Either the file is corrupt, or it has a different layout than this program\n"
! 				 "is expecting.  The results below are untrustworthy.\n\n"));
  
  	/*
  	 * This slightly-chintzy coding will work as long as the control file
--- 156,163 ----
  		exit(1);
  	}
  
!  	/* get a copy of the control file */
!  	ControlFile = get_controlfile(DataDir, progname);
  
  	/*
  	 * This slightly-chintzy coding will work as long as the control file
*************** main(int argc, char *argv[])
*** 199,208 ****
  	 * Use variable for format to suppress overly-anal-retentive gcc warning
  	 * about %c
  	 */
! 	time_tmp = (time_t) ControlFile.time;
  	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
  			 localtime(&time_tmp));
! 	time_tmp = (time_t) ControlFile.checkPointCopy.time;
  	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
  			 localtime(&time_tmp));
  
--- 168,177 ----
  	 * Use variable for format to suppress overly-anal-retentive gcc warning
  	 * about %c
  	 */
! 	time_tmp = (time_t) ControlFile->time;
  	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
  			 localtime(&time_tmp));
! 	time_tmp = (time_t) ControlFile->checkPointCopy.time;
  	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
  			 localtime(&time_tmp));
  
*************** main(int argc, char *argv[])
*** 210,338 ****
  	 * Calculate name of the WAL file containing the latest checkpoint's REDO
  	 * start point.
  	 */
! 	XLByteToSeg(ControlFile.checkPointCopy.redo, segno);
! 	XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno);
  
  	/*
  	 * Format system_identifier separately to keep platform-dependent format
  	 * code out of the translatable message string.
  	 */
  	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
! 			 ControlFile.system_identifier);
  
  	printf(_("pg_control version number:            %u\n"),
! 		   ControlFile.pg_control_version);
! 	if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0)
  		printf(_("WARNING: possible byte ordering mismatch\n"
  				 "The byte ordering used to store the pg_control file might not match the one\n"
  				 "used by this program.  In that case the results below would be incorrect, and\n"
  				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
  	printf(_("Catalog version number:               %u\n"),
! 		   ControlFile.catalog_version_no);
  	printf(_("Database system identifier:           %s\n"),
  		   sysident_str);
  	printf(_("Database cluster state:               %s\n"),
! 		   dbState(ControlFile.state));
  	printf(_("pg_control last modified:             %s\n"),
  		   pgctime_str);
  	printf(_("Latest checkpoint location:           %X/%X\n"),
! 		   (uint32) (ControlFile.checkPoint >> 32),
! 		   (uint32) ControlFile.checkPoint);
  	printf(_("Prior checkpoint location:            %X/%X\n"),
! 		   (uint32) (ControlFile.prevCheckPoint >> 32),
! 		   (uint32) ControlFile.prevCheckPoint);
  	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
! 		   (uint32) (ControlFile.checkPointCopy.redo >> 32),
! 		   (uint32) ControlFile.checkPointCopy.redo);
  	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
  		   xlogfilename);
  	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
! 		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
! 		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
! 		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
  	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
! 		   ControlFile.checkPointCopy.nextXidEpoch,
! 		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
! 		   ControlFile.checkPointCopy.nextOid);
  	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
! 		   ControlFile.checkPointCopy.nextMulti);
  	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
! 		   ControlFile.checkPointCopy.nextMultiOffset);
  	printf(_("Latest checkpoint's oldestXID:        %u\n"),
! 		   ControlFile.checkPointCopy.oldestXid);
  	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
! 		   ControlFile.checkPointCopy.oldestXidDB);
  	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
! 		   ControlFile.checkPointCopy.oldestActiveXid);
  	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
! 		   ControlFile.checkPointCopy.oldestMulti);
  	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
! 		   ControlFile.checkPointCopy.oldestMultiDB);
  	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
! 		   ControlFile.checkPointCopy.oldestCommitTsXid);
  	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
! 		   ControlFile.checkPointCopy.newestCommitTsXid);
  	printf(_("Time of latest checkpoint:            %s\n"),
  		   ckpttime_str);
  	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
! 		   (uint32) (ControlFile.unloggedLSN >> 32),
! 		   (uint32) ControlFile.unloggedLSN);
  	printf(_("Minimum recovery ending location:     %X/%X\n"),
! 		   (uint32) (ControlFile.minRecoveryPoint >> 32),
! 		   (uint32) ControlFile.minRecoveryPoint);
  	printf(_("Min recovery ending loc's timeline:   %u\n"),
! 		   ControlFile.minRecoveryPointTLI);
  	printf(_("Backup start location:                %X/%X\n"),
! 		   (uint32) (ControlFile.backupStartPoint >> 32),
! 		   (uint32) ControlFile.backupStartPoint);
  	printf(_("Backup end location:                  %X/%X\n"),
! 		   (uint32) (ControlFile.backupEndPoint >> 32),
! 		   (uint32) ControlFile.backupEndPoint);
  	printf(_("End-of-backup record required:        %s\n"),
! 		   ControlFile.backupEndRequired ? _("yes") : _("no"));
  	printf(_("wal_level setting:                    %s\n"),
! 		   wal_level_str(ControlFile.wal_level));
  	printf(_("wal_log_hints setting:                %s\n"),
! 		   ControlFile.wal_log_hints ? _("on") : _("off"));
  	printf(_("max_connections setting:              %d\n"),
! 		   ControlFile.MaxConnections);
  	printf(_("max_worker_processes setting:         %d\n"),
! 		   ControlFile.max_worker_processes);
  	printf(_("max_prepared_xacts setting:           %d\n"),
! 		   ControlFile.max_prepared_xacts);
  	printf(_("max_locks_per_xact setting:           %d\n"),
! 		   ControlFile.max_locks_per_xact);
  	printf(_("track_commit_timestamp setting:       %s\n"),
! 		   ControlFile.track_commit_timestamp ? _("on") : _("off"));
  	printf(_("Maximum data alignment:               %u\n"),
! 		   ControlFile.maxAlign);
  	/* we don't print floatFormat since can't say much useful about it */
  	printf(_("Database block size:                  %u\n"),
! 		   ControlFile.blcksz);
  	printf(_("Blocks per segment of large relation: %u\n"),
! 		   ControlFile.relseg_size);
  	printf(_("WAL block size:                       %u\n"),
! 		   ControlFile.xlog_blcksz);
  	printf(_("Bytes per WAL segment:                %u\n"),
! 		   ControlFile.xlog_seg_size);
  	printf(_("Maximum length of identifiers:        %u\n"),
! 		   ControlFile.nameDataLen);
  	printf(_("Maximum columns in an index:          %u\n"),
! 		   ControlFile.indexMaxKeys);
  	printf(_("Maximum size of a TOAST chunk:        %u\n"),
! 		   ControlFile.toast_max_chunk_size);
  	printf(_("Size of a large-object chunk:         %u\n"),
! 		   ControlFile.loblksize);
  	printf(_("Date/time type storage:               %s\n"),
! 		   (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
  	printf(_("Float4 argument passing:              %s\n"),
! 		   (ControlFile.float4ByVal ? _("by value") : _("by reference")));
  	printf(_("Float8 argument passing:              %s\n"),
! 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
  	printf(_("Data page checksum version:           %u\n"),
! 		   ControlFile.data_checksum_version);
  	return 0;
  }
--- 179,307 ----
  	 * Calculate name of the WAL file containing the latest checkpoint's REDO
  	 * start point.
  	 */
! 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
! 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
  
  	/*
  	 * Format system_identifier separately to keep platform-dependent format
  	 * code out of the translatable message string.
  	 */
  	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
! 			 ControlFile->system_identifier);
  
  	printf(_("pg_control version number:            %u\n"),
! 		   ControlFile->pg_control_version);
! 	if (ControlFile->pg_control_version % 65536 == 0 && ControlFile->pg_control_version / 65536 != 0)
  		printf(_("WARNING: possible byte ordering mismatch\n"
  				 "The byte ordering used to store the pg_control file might not match the one\n"
  				 "used by this program.  In that case the results below would be incorrect, and\n"
  				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
  	printf(_("Catalog version number:               %u\n"),
! 		   ControlFile->catalog_version_no);
  	printf(_("Database system identifier:           %s\n"),
  		   sysident_str);
  	printf(_("Database cluster state:               %s\n"),
! 		   dbState(ControlFile->state));
  	printf(_("pg_control last modified:             %s\n"),
  		   pgctime_str);
  	printf(_("Latest checkpoint location:           %X/%X\n"),
! 		   (uint32) (ControlFile->checkPoint >> 32),
! 		   (uint32) ControlFile->checkPoint);
  	printf(_("Prior checkpoint location:            %X/%X\n"),
! 		   (uint32) (ControlFile->prevCheckPoint >> 32),
! 		   (uint32) ControlFile->prevCheckPoint);
  	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
! 		   (uint32) (ControlFile->checkPointCopy.redo >> 32),
! 		   (uint32) ControlFile->checkPointCopy.redo);
  	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
  		   xlogfilename);
  	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
! 		   ControlFile->checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
! 		   ControlFile->checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
! 		   ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
  	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
! 		   ControlFile->checkPointCopy.nextXidEpoch,
! 		   ControlFile->checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
! 		   ControlFile->checkPointCopy.nextOid);
  	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
! 		   ControlFile->checkPointCopy.nextMulti);
  	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
! 		   ControlFile->checkPointCopy.nextMultiOffset);
  	printf(_("Latest checkpoint's oldestXID:        %u\n"),
! 		   ControlFile->checkPointCopy.oldestXid);
  	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
! 		   ControlFile->checkPointCopy.oldestXidDB);
  	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
! 		   ControlFile->checkPointCopy.oldestActiveXid);
  	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
! 		   ControlFile->checkPointCopy.oldestMulti);
  	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
! 		   ControlFile->checkPointCopy.oldestMultiDB);
  	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
! 		   ControlFile->checkPointCopy.oldestCommitTsXid);
  	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
! 		   ControlFile->checkPointCopy.newestCommitTsXid);
  	printf(_("Time of latest checkpoint:            %s\n"),
  		   ckpttime_str);
  	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
! 		   (uint32) (ControlFile->unloggedLSN >> 32),
! 		   (uint32) ControlFile->unloggedLSN);
  	printf(_("Minimum recovery ending location:     %X/%X\n"),
! 		   (uint32) (ControlFile->minRecoveryPoint >> 32),
! 		   (uint32) ControlFile->minRecoveryPoint);
  	printf(_("Min recovery ending loc's timeline:   %u\n"),
! 		   ControlFile->minRecoveryPointTLI);
  	printf(_("Backup start location:                %X/%X\n"),
! 		   (uint32) (ControlFile->backupStartPoint >> 32),
! 		   (uint32) ControlFile->backupStartPoint);
  	printf(_("Backup end location:                  %X/%X\n"),
! 		   (uint32) (ControlFile->backupEndPoint >> 32),
! 		   (uint32) ControlFile->backupEndPoint);
  	printf(_("End-of-backup record required:        %s\n"),
! 		   ControlFile->backupEndRequired ? _("yes") : _("no"));
  	printf(_("wal_level setting:                    %s\n"),
! 		   wal_level_str(ControlFile->wal_level));
  	printf(_("wal_log_hints setting:                %s\n"),
! 		   ControlFile->wal_log_hints ? _("on") : _("off"));
  	printf(_("max_connections setting:              %d\n"),
! 		   ControlFile->MaxConnections);
  	printf(_("max_worker_processes setting:         %d\n"),
! 		   ControlFile->max_worker_processes);
  	printf(_("max_prepared_xacts setting:           %d\n"),
! 		   ControlFile->max_prepared_xacts);
  	printf(_("max_locks_per_xact setting:           %d\n"),
! 		   ControlFile->max_locks_per_xact);
  	printf(_("track_commit_timestamp setting:       %s\n"),
! 		   ControlFile->track_commit_timestamp ? _("on") : _("off"));
  	printf(_("Maximum data alignment:               %u\n"),
! 		   ControlFile->maxAlign);
  	/* we don't print floatFormat since can't say much useful about it */
  	printf(_("Database block size:                  %u\n"),
! 		   ControlFile->blcksz);
  	printf(_("Blocks per segment of large relation: %u\n"),
! 		   ControlFile->relseg_size);
  	printf(_("WAL block size:                       %u\n"),
! 		   ControlFile->xlog_blcksz);
  	printf(_("Bytes per WAL segment:                %u\n"),
! 		   ControlFile->xlog_seg_size);
  	printf(_("Maximum length of identifiers:        %u\n"),
! 		   ControlFile->nameDataLen);
  	printf(_("Maximum columns in an index:          %u\n"),
! 		   ControlFile->indexMaxKeys);
  	printf(_("Maximum size of a TOAST chunk:        %u\n"),
! 		   ControlFile->toast_max_chunk_size);
  	printf(_("Size of a large-object chunk:         %u\n"),
! 		   ControlFile->loblksize);
  	printf(_("Date/time type storage:               %s\n"),
! 		   (ControlFile->enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
  	printf(_("Float4 argument passing:              %s\n"),
! 		   (ControlFile->float4ByVal ? _("by value") : _("by reference")));
  	printf(_("Float8 argument passing:              %s\n"),
! 		   (ControlFile->float8ByVal ? _("by value") : _("by reference")));
  	printf(_("Data page checksum version:           %u\n"),
! 		   ControlFile->data_checksum_version);
  	return 0;
  }
diff --git a/src/common/Makefile b/src/common/Makefile
index bde4fc2..f7a4a4d 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** override CPPFLAGS += -DVAL_LDFLAGS_EX="\
*** 36,43 ****
  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
  
--- 36,43 ----
  override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
  override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
  
! OBJS_COMMON = config_info.o controldata_utils.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
  
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index ...a0204a7 .
*** a/src/common/controldata_utils.c
--- b/src/common/controldata_utils.c
***************
*** 0 ****
--- 1,88 ----
+ /*-------------------------------------------------------------------------
+  *
+  * controldata_utils.c
+  *		Common code for pg_controldata output
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+ 
+ #include <unistd.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ 
+ #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
+ #include "port/pg_crc32c.h"
+ 
+ #ifndef FRONTEND
+ /* NOTE: caller must provide gettext call around str */
+ #define log_error(str, param1, param2)	\
+ 	elog(ERROR, str, param1, param2)
+ #else
+ #define log_error(str, param1, param2)	\
+ 	do { \
+ 			char *buf = psprintf("%%s: %s", str); \
+ 			fprintf(stderr, buf, progname, param1, param2); \
+ 			exit(2); \
+ 	} while (0)
+ #endif
+ 
+ /*
+  * get_controlfile(char *DataDir, const char *progname)
+  *
+  * Get controlfile values. The caller is responsible
+  * for pfreeing the result.
+  */
+ ControlFileData *
+ get_controlfile(char *DataDir, const char *progname)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	int					fd;
+ 	char				ControlFilePath[MAXPGPATH];
+ 	pg_crc32c			crc;
+ 
+ 	ControlFile = palloc(sizeof(ControlFileData));
+ 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+ 
+ 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+ 		log_error(_("could not open file \"%s\" for reading: %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+ 		log_error(_("could not read file \"%s\": %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	close(fd);
+ 
+ 	/* Check the CRC. */
+ 	INIT_CRC32C(crc);
+ 	COMP_CRC32C(crc,
+ 			   (char *) ControlFile,
+ 			   offsetof(ControlFileData, crc));
+ 	FIN_CRC32C(crc);
+ 
+ 	if (!EQ_CRC32C(crc, ControlFile->crc))
+ #ifndef FRONTEND
+ 		elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
+ #else
+ 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+ 				 "Either the file is corrupt, or it has a different layout than this program\n"
+ 				 "is expecting.  The results below are untrustworthy.\n\n"));
+ #endif
+ 
+ 	return ControlFile;
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 62b9125..e22bfd3 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("row security for current context
*** 5216,5221 ****
--- 5216,5234 ----
  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");
  
+ /* pg_controldata related functions */
+ DATA(insert OID = 3441 ( pg_control_system PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,20,1184}" "{o,o,o,o}" "{pg_control_version,catalog_version_no,system_identifier,pg_control_last_modified}" _null_ _null_ pg_control_system _null_ _null_ _null_ ));
+ DESCR("pg_controldata general state information as a function");
+ 
+ DATA(insert OID = 3442 ( pg_control_checkpoint PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,3220,3220,25,23,23,16,25,26,28,28,28,26,28,28,26,28,28,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{checkpoint_location,prior_location,redo_location,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}" _null_ _null_ pg_control_checkpoint _null_ _null_ _null_ ));
+ DESCR("pg_controldata checkpoint state information as a function");
+ 
+ DATA(insert OID = 3443 ( pg_control_recovery PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,23,3220,3220,16}" "{o,o,o,o,o}" "{min_recovery_end_location,min_recovery_end_timeline,backup_start_location,backup_end_location,end_of_backup_record_required}" _null_ _null_ pg_control_recovery _null_ _null_ _null_ ));
+ DESCR("pg_controldata recovery state information as a function");
+ 
+ DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
+ DESCR("pg_controldata init state information 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/controldata_utils.h b/src/include/common/controldata_utils.h
index ...1a09765 .
*** a/src/include/common/controldata_utils.h
--- b/src/include/common/controldata_utils.h
***************
*** 0 ****
--- 1,15 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  *	Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *	src/include/common/controldata_utils.h
+  */
+ #ifndef COMMON_CONTROLDATA_UTILS_H
+ #define COMMON_CONTROLDATA_UTILS_H
+ 
+ extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+ 
+ #endif   /* COMMON_CONTROLDATA_UTILS_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7ec93c9..115f8af 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum show_all_file_settings(PG_F
*** 1151,1156 ****
--- 1151,1162 ----
  /* pg_config.c */
  extern Datum pg_config(PG_FUNCTION_ARGS);
  
+ /* pg_controldata.c */
+ extern Datum pg_control_checkpoint(PG_FUNCTION_ARGS);
+ extern Datum pg_control_system(PG_FUNCTION_ARGS);
+ extern Datum pg_control_init(PG_FUNCTION_ARGS);
+ extern Datum pg_control_recovery(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/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4fb44e..1a9c996 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,112 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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 = (
--- 106,112 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  config_info.c controldata_utils.c exec.c pg_lzcompress.c pgfnames.c
  	  relpath.c rmtree.c string.c username.c wait_error.c);
  
  	our @pgcommonfrontendfiles = (
#103Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#102)
Re: exposing pg_controldata and pg_config as functions

On Sun, Feb 28, 2016 at 9:33 AM, Joe Conway <mail@joeconway.com> wrote:

On 02/21/2016 05:30 AM, Michael Paquier wrote:

Looking again at this thread I guess that this is consensus, based on
the proposal from Josh and seeing no other ideas around. Another idea
would be to group all the fields that into a single function
pg_control_data().

I think a single function would be ridiculously wide. I like the four
separate functions better if we're going to do it this way at all.

+   <indexterm>
+    <primary>pg_checkpoint_state</primary>
+   </indexterm>
+   <para>
+    <function>pg_checkpoint_state</> returns a record containing
+    checkpoint_location, prior_location, redo_location, redo_wal_file,
+    timeline_id, prev_timeline_id, full_page_writes, next_xid, next_oid,
+    next_multixact_id, next_multi_offset, oldest_xid, oldest_xid_dbid,
+    oldest_active_xid, oldest_multi_xid, oldest_multi_dbid,
+    oldest_commit_ts_xid, newest_commit_ts_xid, and checkpoint_time.
+   </para>
This is bit unreadable. The only entry in the documentation that
adopts a similar style is pg_stat_file, and with six fields that feels
as being enough. I would suggest using a table instead with the type
of the field and its name.

Ok, changed to your suggestion.

Regarding the naming of the functions, I think that it would be good
to get something consistent with the concept of those being "Control
Data functions" by having them share the same prefix, say pg_control_
- pg_control_checkpoint
- pg_control_init
- pg_control_system
- pg_control_recovery

No issues -- changed.

+       snprintf (controldata_name, CONTROLDATANAME_LEN,
+                 "%s:", controldata[i].name);
Nitpick: extra space.

I didn't understand this comment but it is moot now anyway...

+static const char *const controldata_names[] =
+{
+   gettext_noop("pg_control version number"),
+   gettext_noop("Catalog version number"),
+   gettext_noop("Database system identifier"),
Is this complication really necessary? Those identifiers are used only
in the frontend and the footprint of this patch on pg_controldata is
really large. What I think we should do is have in src/common the
following set of routines that work directly on ControlFileData:
- checkControlFile, to perform basic sanity checks on the control file
(CRC, see for example pg_rewind.c)
- getControlFile(dataDir), that simply returns a palloc'd
ControlFileData to the caller after looking at global/pg_control.
pg_rewind could perhaps make use of the one to check the control file
CRC, to fetch ControlFileData there is some parallel logic for the
source server if it is either remote or local so it would be better to
not use getControlFile in this case.

I agree with the assessment that much of what had been moved based on
the original pg_controladata() SRF no longer needs to move. This version
only puts get_controlfile() into src/common, since that is the bit that
is still shared. If checkControlFile() or something similar is useful
for pg_rewind or some other extension, I'd say that should be a separate
patch.

Oh, and the entire thing is now rebased against a git pull from a few
hours ago. I moved this to the upcoming commitfest too, although I think
it is pretty well ready to go.

Thanks for the updated version.

+ Returns information about current controldata file state.
s/controldata/control data?

+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Column Name</entry>
+       <entry>Data Type</entry>
+      </row>
+     </thead>
+
Having a description of each field would be nice.
+ * pg_controldata.c
+ *     Expose select pg_controldata output, except via SQL functions
I am not much getting the meaning of this sentence. What about the following:
"Set of routines exposing the contents of the control data file in a
set of SQL functions".
+   /* Populate the values and null arrays */
+   values[0] = LSNGetDatum(ControlFile->checkPoint);
+   nulls[0] = false;
+
+   values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+   nulls[1] = false;
Instead of setting each flag of nulls[] one by one, just calling once
MemSet would be fine. Any method is fine though.
+   /* get a copy of the control file */
+   ControlFile = get_controlfile(DataDir, progname);
Some whitespaces here. git diff is showing in red here.
+   if (ControlFile->pg_control_version % 65536 == 0 &&
+       ControlFile->pg_control_version / 65536 != 0)
+       elog(ERROR, _("byte ordering mismatch"));
You may want to put this check directly in get_controlfile(). it is
repeated 4 times in the backend, and has an equivalent in
pg_controldata.
    our @pgcommonallfiles = qw(
-     config_info.c exec.c pg_lzcompress.c pgfnames.c psprintf.c
+     config_info.c controldata_utils.c exec.c pg_lzcompress.c pgfnames.c
      relpath.c rmtree.c string.c username.c wait_error.c);
"psprintf.c" has been removed from this list. This causes the build
with MSVC to fail.
-- 
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#104Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#103)
1 attachment(s)
Re: exposing pg_controldata and pg_config as functions

On 02/28/2016 05:16 AM, Michael Paquier wrote:

+ Returns information about current controldata file state.
s/controldata/control data?

+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Column Name</entry>
+       <entry>Data Type</entry>
+      </row>
+     </thead>
+
Having a description of each field would be nice.

Might be nice, but the pg_controldata section of the docs does not have
any description either, and I don't feel compelled to improve on that
just for the sake of this patch. I'll put it on my TODO to improve both
at some point though.

+ * pg_controldata.c
+ *     Expose select pg_controldata output, except via SQL functions
I am not much getting the meaning of this sentence. What about the following:
"Set of routines exposing the contents of the control data file in a
set of SQL functions".

Ok, reworded to something similar.

+   /* Populate the values and null arrays */
+   values[0] = LSNGetDatum(ControlFile->checkPoint);
+   nulls[0] = false;
+
+   values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+   nulls[1] = false;
Instead of setting each flag of nulls[] one by one, just calling once
MemSet would be fine. Any method is fine though.

I prefer the current style and I believe it is more consistent
with what is done elsewhere. In any case this is not exactly a
performance critical path.

+   /* get a copy of the control file */
+   ControlFile = get_controlfile(DataDir, progname);
Some whitespaces here. git diff is showing in red here.

fixed

+   if (ControlFile->pg_control_version % 65536 == 0 &&
+       ControlFile->pg_control_version / 65536 != 0)
+       elog(ERROR, _("byte ordering mismatch"));
You may want to put this check directly in get_controlfile(). it is
repeated 4 times in the backend, and has an equivalent in
pg_controldata.

makes sense - done

our @pgcommonallfiles = qw(
-     config_info.c exec.c pg_lzcompress.c pgfnames.c psprintf.c
+     config_info.c controldata_utils.c exec.c pg_lzcompress.c pgfnames.c
relpath.c rmtree.c string.c username.c wait_error.c);
"psprintf.c" has been removed from this list. This causes the build
with MSVC to fail.

good catch -- fixed

If there are no other complaints or comments, I will commit the attached
sometime this coming week (the the requisite catversion bump).

Thanks!

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development

Attachments:

pg_controldata_funcs-2016.02.28.00.difftext/x-diff; name=pg_controldata_funcs-2016.02.28.00.diffDownload
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c0b94bc..4b5ee81 100644
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
*************** SELECT collation for ('foo' COLLATE "de_
*** 16703,16708 ****
--- 16703,17064 ----
      </tgroup>
     </table>
  
+    <para>
+     The functions shown in <xref linkend="functions-controldata">
+     print information initialized during <command>initdb</>, such
+     as the catalog version. They also show information about write-ahead
+     logging and checkpoint processing. This information is cluster-wide,
+     and not specific to any one database. They provide most of the same
+     information, from the same source, as
+     <xref linkend="APP-PGCONTROLDATA">, although in a form better suited
+     to <acronym>SQL</acronym> functions.
+    </para>
+ 
+    <table id="functions-controldata">
+     <title>Control Data Functions</title>
+     <tgroup cols="3">
+      <thead>
+       <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+      </thead>
+ 
+      <tbody>
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_checkpoint</primary></indexterm>
+         <literal><function>pg_control_checkpoint()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current checkpoint state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_system</primary></indexterm>
+         <literal><function>pg_control_system()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about current controldata file state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_init</primary></indexterm>
+         <literal><function>pg_control_init()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about cluster initialization state.
+        </entry>
+       </row>
+ 
+       <row>
+        <entry>
+         <indexterm><primary>pg_control_recovery</primary></indexterm>
+         <literal><function>pg_control_recovery()</function></literal>
+        </entry>
+        <entry><type>record</type></entry>
+        <entry>
+         Returns information about recovery state.
+        </entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_checkpoint</> returns a record, shown in
+     <xref linkend="functions-pg-control-checkpoint">
+    </para>
+ 
+    <table id="functions-pg-control-checkpoint">
+     <title><function>pg_control_checkpoint</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>checkpoint_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>prior_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>redo_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>redo_wal_file</entry>
+        <entry><type>text</type></entry>
+       </row>
+ 
+       <row>
+        <entry>timeline_id</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>prev_timeline_id</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>full_page_writes</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_xid</entry>
+        <entry><type>text</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_oid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_multixact_id</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>next_multi_offset</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_xid_dbid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_active_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_multi_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_multi_dbid</entry>
+        <entry><type>oid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>oldest_commit_ts_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>newest_commit_ts_xid</entry>
+        <entry><type>xid</type></entry>
+       </row>
+ 
+       <row>
+        <entry>checkpoint_time</entry>
+        <entry><type>timestamp with time zone</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_system</> returns a record, shown in
+     <xref linkend="functions-pg-control-system">
+    </para>
+ 
+    <table id="functions-pg-control-system">
+     <title><function>pg_control_system</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>pg_control_version</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>catalog_version_no</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>system_identifier</entry>
+        <entry><type>bigint</type></entry>
+       </row>
+ 
+       <row>
+        <entry>pg_control_last_modified</entry>
+        <entry><type>timestamp with time zone</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_init</> returns a record, shown in
+     <xref linkend="functions-pg-control-init">
+    </para>
+ 
+    <table id="functions-pg-control-init">
+     <title><function>pg_control_init</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>max_data_alignment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>database_block_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>blocks_per_segment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>wal_block_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>bytes_per_wal_segment</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_identifier_length</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_index_columns</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>max_toast_chunk_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>large_object_chunk_size</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>bigint_timestamps</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>float4_pass_by_value</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>float8_pass_by_value</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+       <row>
+        <entry>data_page_checksum_version</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
+    <para>
+     <function>pg_control_recovery</> returns a record, shown in
+     <xref linkend="functions-pg-control-recovery">
+    </para>
+ 
+    <table id="functions-pg-control-recovery">
+     <title><function>pg_control_recovery</> Columns</title>
+     <tgroup cols="2">
+      <thead>
+       <row>
+        <entry>Column Name</entry>
+        <entry>Data Type</entry>
+       </row>
+      </thead>
+ 
+      <tbody>
+ 
+       <row>
+        <entry>min_recovery_end_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>min_recovery_end_timeline</entry>
+        <entry><type>integer</type></entry>
+       </row>
+ 
+       <row>
+        <entry>backup_start_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>backup_end_location</entry>
+        <entry><type>pg_lsn</type></entry>
+       </row>
+ 
+       <row>
+        <entry>end_of_backup_record_required</entry>
+        <entry><type>boolean</type></entry>
+       </row>
+ 
+      </tbody>
+     </tgroup>
+    </table>
+ 
    </sect1>
  
    <sect1 id="functions-admin">
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index a0c82c1..a5b487d 100644
*** a/src/backend/utils/misc/Makefile
--- b/src/backend/utils/misc/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,20 ****
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! 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
--- 14,20 ----
  
  override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
  
! OBJS = guc.o help_config.o pg_config.o pg_controldata.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
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index ...8552c07 .
*** a/src/backend/utils/misc/pg_controldata.c
--- b/src/backend/utils/misc/pg_controldata.c
***************
*** 0 ****
--- 1,341 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_controldata.c
+  *
+  * Routines to expose the contents of the control data file via
+  * a set of SQL functions.
+  *
+  * 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_controldata.c
+  *-------------------------------------------------------------------------
+  */
+ 
+ #include "postgres.h"
+ 
+ #include "funcapi.h"
+ #include "miscadmin.h"
+ #include "access/htup_details.h"
+ #include "access/xlog_internal.h"
+ #include "catalog/pg_control.h"
+ #include "catalog/pg_type.h"
+ #include "common/controldata_utils.h"
+ #include "utils/builtins.h"
+ #include "utils/pg_lsn.h"
+ #include "utils/timestamp.h"
+ 
+ Datum
+ pg_control_system(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[4];
+ 	bool				nulls[4];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(4, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier",
+ 					   INT8OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	values[0] = Int32GetDatum(ControlFile->pg_control_version);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->catalog_version_no);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int64GetDatum(ControlFile->system_identifier);
+ 	nulls[2] = false;
+ 
+ 	values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
+ 	nulls[3] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_checkpoint(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[19];
+ 	bool				nulls[19];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 	XLogSegNo			segno;
+ 	char				xlogfilename[MAXFNAMELEN];
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(19, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
+ 					   OIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
+ 					   XIDOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
+ 					   TIMESTAMPTZOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* Read the control file. */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	/*
+ 	 * Calculate name of the WAL file containing the latest checkpoint's REDO
+ 	 * start point.
+ 	 */
+ 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
+ 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
+ 
+ 	/* Populate the values and null arrays */
+ 	values[0] = LSNGetDatum(ControlFile->checkPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
+ 	nulls[2] = false;
+ 
+ 	values[3] = CStringGetTextDatum(xlogfilename);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
+ 	nulls[5] = false;
+ 
+ 	values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
+ 	nulls[6] = false;
+ 
+ 	values[7] = CStringGetTextDatum(psprintf("%u:%u",
+ 								ControlFile->checkPointCopy.nextXidEpoch,
+ 								ControlFile->checkPointCopy.nextXid));
+ 	nulls[7] = false;
+ 
+ 	values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
+ 	nulls[8] = false;
+ 
+ 	values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
+ 	nulls[9] = false;
+ 
+ 	values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
+ 	nulls[10] = false;
+ 
+ 	values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
+ 	nulls[11] = false;
+ 
+ 	values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
+ 	nulls[12] = false;
+ 
+ 	values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
+ 	nulls[13] = false;
+ 
+ 	values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
+ 	nulls[14] = false;
+ 
+ 	values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
+ 	nulls[15] = false;
+ 
+ 	values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
+ 	nulls[16] = false;
+ 
+ 	values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
+ 	nulls[17] = false;
+ 
+ 	values[18] = TimestampTzGetDatum(
+ 					time_t_to_timestamptz(ControlFile->checkPointCopy.time));
+ 	nulls[18] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_recovery(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[5];
+ 	bool				nulls[5];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(5, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_location",
+ 					   LSNOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
+ 					   BOOLOID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
+ 	nulls[1] = false;
+ 
+ 	values[2] = LSNGetDatum(ControlFile->backupStartPoint);
+ 	nulls[2] = false;
+ 
+ 	values[3] = LSNGetDatum(ControlFile->backupEndPoint);
+ 	nulls[3] = false;
+ 
+ 	values[4] = BoolGetDatum(ControlFile->backupEndRequired);
+ 	nulls[4] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
+ 
+ Datum
+ pg_control_init(PG_FUNCTION_ARGS)
+ {
+ 	Datum				values[13];
+ 	bool				nulls[13];
+ 	TupleDesc			tupdesc;
+ 	HeapTuple			htup;
+ 	ControlFileData	   *ControlFile;
+ 
+ 	/*
+ 	 * Construct a tuple descriptor for the result row.  This must match this
+ 	 * function's pg_proc entry!
+ 	 */
+ 	tupdesc = CreateTemplateTupleDesc(13, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size",
+ 					   INT4OID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 10, "bigint_timestamps",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float4_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 12, "float8_pass_by_value",
+ 					   BOOLOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 13, "data_page_checksum_version",
+ 					   INT4OID, -1, 0);
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	/* read the control file */
+ 	ControlFile = get_controlfile(DataDir, NULL);
+ 
+ 	values[0] = Int32GetDatum(ControlFile->maxAlign);
+ 	nulls[0] = false;
+ 
+ 	values[1] = Int32GetDatum(ControlFile->blcksz);
+ 	nulls[1] = false;
+ 
+ 	values[2] = Int32GetDatum(ControlFile->relseg_size);
+ 	nulls[2] = false;
+ 
+ 	values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
+ 	nulls[3] = false;
+ 
+ 	values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
+ 	nulls[4] = false;
+ 
+ 	values[5] = Int32GetDatum(ControlFile->nameDataLen);
+ 	nulls[5] = false;
+ 
+ 	values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
+ 	nulls[6] = false;
+ 
+ 	values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
+ 	nulls[7] = false;
+ 
+ 	values[8] = Int32GetDatum(ControlFile->loblksize);
+ 	nulls[8] = false;
+ 
+ 	values[9] = BoolGetDatum(ControlFile->enableIntTimes);
+ 	nulls[9] = false;
+ 
+ 	values[10] = BoolGetDatum(ControlFile->float4ByVal);
+ 	nulls[10] = false;
+ 
+ 	values[11] = BoolGetDatum(ControlFile->float8ByVal);
+ 	nulls[11] = false;
+ 
+ 	values[12] = Int32GetDatum(ControlFile->data_checksum_version);
+ 	nulls[12] = false;
+ 
+ 	htup = heap_form_tuple(tupdesc, values, nulls);
+ 
+ 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+ }
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 5dd2dbc..fdf7c7d 100644
*** a/src/bin/pg_controldata/pg_controldata.c
--- b/src/bin/pg_controldata/pg_controldata.c
***************
*** 18,31 ****
  
  #include "postgres.h"
  
- #include <unistd.h>
  #include <time.h>
- #include <sys/stat.h>
- #include <fcntl.h>
  
  #include "access/xlog.h"
  #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
  #include "pg_getopt.h"
  
  
--- 18,29 ----
  
  #include "postgres.h"
  
  #include <time.h>
  
  #include "access/xlog.h"
  #include "access/xlog_internal.h"
  #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
  #include "pg_getopt.h"
  
  
*************** wal_level_str(WalLevel wal_level)
*** 89,99 ****
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData ControlFile;
! 	int			fd;
! 	char		ControlFilePath[MAXPGPATH];
  	char	   *DataDir = NULL;
- 	pg_crc32c	crc;
  	time_t		time_tmp;
  	char		pgctime_str[128];
  	char		ckpttime_str[128];
--- 87,94 ----
  int
  main(int argc, char *argv[])
  {
! 	ControlFileData *ControlFile;
  	char	   *DataDir = NULL;
  	time_t		time_tmp;
  	char		pgctime_str[128];
  	char		ckpttime_str[128];
*************** main(int argc, char *argv[])
*** 161,194 ****
  		exit(1);
  	}
  
! 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
! 
! 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
! 	{
! 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 
! 	if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
! 	{
! 		fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
! 				progname, ControlFilePath, strerror(errno));
! 		exit(2);
! 	}
! 	close(fd);
! 
! 	/* Check the CRC. */
! 	INIT_CRC32C(crc);
! 	COMP_CRC32C(crc,
! 				(char *) &ControlFile,
! 				offsetof(ControlFileData, crc));
! 	FIN_CRC32C(crc);
! 
! 	if (!EQ_CRC32C(crc, ControlFile.crc))
! 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
! 				 "Either the file is corrupt, or it has a different layout than this program\n"
! 				 "is expecting.  The results below are untrustworthy.\n\n"));
  
  	/*
  	 * This slightly-chintzy coding will work as long as the control file
--- 156,163 ----
  		exit(1);
  	}
  
! 	/* get a copy of the control file */
! 	ControlFile = get_controlfile(DataDir, progname);
  
  	/*
  	 * This slightly-chintzy coding will work as long as the control file
*************** main(int argc, char *argv[])
*** 199,208 ****
  	 * Use variable for format to suppress overly-anal-retentive gcc warning
  	 * about %c
  	 */
! 	time_tmp = (time_t) ControlFile.time;
  	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
  			 localtime(&time_tmp));
! 	time_tmp = (time_t) ControlFile.checkPointCopy.time;
  	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
  			 localtime(&time_tmp));
  
--- 168,177 ----
  	 * Use variable for format to suppress overly-anal-retentive gcc warning
  	 * about %c
  	 */
! 	time_tmp = (time_t) ControlFile->time;
  	strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
  			 localtime(&time_tmp));
! 	time_tmp = (time_t) ControlFile->checkPointCopy.time;
  	strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
  			 localtime(&time_tmp));
  
*************** main(int argc, char *argv[])
*** 210,338 ****
  	 * Calculate name of the WAL file containing the latest checkpoint's REDO
  	 * start point.
  	 */
! 	XLByteToSeg(ControlFile.checkPointCopy.redo, segno);
! 	XLogFileName(xlogfilename, ControlFile.checkPointCopy.ThisTimeLineID, segno);
  
  	/*
  	 * Format system_identifier separately to keep platform-dependent format
  	 * code out of the translatable message string.
  	 */
  	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
! 			 ControlFile.system_identifier);
  
  	printf(_("pg_control version number:            %u\n"),
! 		   ControlFile.pg_control_version);
! 	if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0)
! 		printf(_("WARNING: possible byte ordering mismatch\n"
! 				 "The byte ordering used to store the pg_control file might not match the one\n"
! 				 "used by this program.  In that case the results below would be incorrect, and\n"
! 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
  	printf(_("Catalog version number:               %u\n"),
! 		   ControlFile.catalog_version_no);
  	printf(_("Database system identifier:           %s\n"),
  		   sysident_str);
  	printf(_("Database cluster state:               %s\n"),
! 		   dbState(ControlFile.state));
  	printf(_("pg_control last modified:             %s\n"),
  		   pgctime_str);
  	printf(_("Latest checkpoint location:           %X/%X\n"),
! 		   (uint32) (ControlFile.checkPoint >> 32),
! 		   (uint32) ControlFile.checkPoint);
  	printf(_("Prior checkpoint location:            %X/%X\n"),
! 		   (uint32) (ControlFile.prevCheckPoint >> 32),
! 		   (uint32) ControlFile.prevCheckPoint);
  	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
! 		   (uint32) (ControlFile.checkPointCopy.redo >> 32),
! 		   (uint32) ControlFile.checkPointCopy.redo);
  	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
  		   xlogfilename);
  	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
! 		   ControlFile.checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
! 		   ControlFile.checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
! 		   ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
  	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
! 		   ControlFile.checkPointCopy.nextXidEpoch,
! 		   ControlFile.checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
! 		   ControlFile.checkPointCopy.nextOid);
  	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
! 		   ControlFile.checkPointCopy.nextMulti);
  	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
! 		   ControlFile.checkPointCopy.nextMultiOffset);
  	printf(_("Latest checkpoint's oldestXID:        %u\n"),
! 		   ControlFile.checkPointCopy.oldestXid);
  	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
! 		   ControlFile.checkPointCopy.oldestXidDB);
  	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
! 		   ControlFile.checkPointCopy.oldestActiveXid);
  	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
! 		   ControlFile.checkPointCopy.oldestMulti);
  	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
! 		   ControlFile.checkPointCopy.oldestMultiDB);
  	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
! 		   ControlFile.checkPointCopy.oldestCommitTsXid);
  	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
! 		   ControlFile.checkPointCopy.newestCommitTsXid);
  	printf(_("Time of latest checkpoint:            %s\n"),
  		   ckpttime_str);
  	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
! 		   (uint32) (ControlFile.unloggedLSN >> 32),
! 		   (uint32) ControlFile.unloggedLSN);
  	printf(_("Minimum recovery ending location:     %X/%X\n"),
! 		   (uint32) (ControlFile.minRecoveryPoint >> 32),
! 		   (uint32) ControlFile.minRecoveryPoint);
  	printf(_("Min recovery ending loc's timeline:   %u\n"),
! 		   ControlFile.minRecoveryPointTLI);
  	printf(_("Backup start location:                %X/%X\n"),
! 		   (uint32) (ControlFile.backupStartPoint >> 32),
! 		   (uint32) ControlFile.backupStartPoint);
  	printf(_("Backup end location:                  %X/%X\n"),
! 		   (uint32) (ControlFile.backupEndPoint >> 32),
! 		   (uint32) ControlFile.backupEndPoint);
  	printf(_("End-of-backup record required:        %s\n"),
! 		   ControlFile.backupEndRequired ? _("yes") : _("no"));
  	printf(_("wal_level setting:                    %s\n"),
! 		   wal_level_str(ControlFile.wal_level));
  	printf(_("wal_log_hints setting:                %s\n"),
! 		   ControlFile.wal_log_hints ? _("on") : _("off"));
  	printf(_("max_connections setting:              %d\n"),
! 		   ControlFile.MaxConnections);
  	printf(_("max_worker_processes setting:         %d\n"),
! 		   ControlFile.max_worker_processes);
  	printf(_("max_prepared_xacts setting:           %d\n"),
! 		   ControlFile.max_prepared_xacts);
  	printf(_("max_locks_per_xact setting:           %d\n"),
! 		   ControlFile.max_locks_per_xact);
  	printf(_("track_commit_timestamp setting:       %s\n"),
! 		   ControlFile.track_commit_timestamp ? _("on") : _("off"));
  	printf(_("Maximum data alignment:               %u\n"),
! 		   ControlFile.maxAlign);
  	/* we don't print floatFormat since can't say much useful about it */
  	printf(_("Database block size:                  %u\n"),
! 		   ControlFile.blcksz);
  	printf(_("Blocks per segment of large relation: %u\n"),
! 		   ControlFile.relseg_size);
  	printf(_("WAL block size:                       %u\n"),
! 		   ControlFile.xlog_blcksz);
  	printf(_("Bytes per WAL segment:                %u\n"),
! 		   ControlFile.xlog_seg_size);
  	printf(_("Maximum length of identifiers:        %u\n"),
! 		   ControlFile.nameDataLen);
  	printf(_("Maximum columns in an index:          %u\n"),
! 		   ControlFile.indexMaxKeys);
  	printf(_("Maximum size of a TOAST chunk:        %u\n"),
! 		   ControlFile.toast_max_chunk_size);
  	printf(_("Size of a large-object chunk:         %u\n"),
! 		   ControlFile.loblksize);
  	printf(_("Date/time type storage:               %s\n"),
! 		   (ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
  	printf(_("Float4 argument passing:              %s\n"),
! 		   (ControlFile.float4ByVal ? _("by value") : _("by reference")));
  	printf(_("Float8 argument passing:              %s\n"),
! 		   (ControlFile.float8ByVal ? _("by value") : _("by reference")));
  	printf(_("Data page checksum version:           %u\n"),
! 		   ControlFile.data_checksum_version);
  	return 0;
  }
--- 179,302 ----
  	 * Calculate name of the WAL file containing the latest checkpoint's REDO
  	 * start point.
  	 */
! 	XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
! 	XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
  
  	/*
  	 * Format system_identifier separately to keep platform-dependent format
  	 * code out of the translatable message string.
  	 */
  	snprintf(sysident_str, sizeof(sysident_str), UINT64_FORMAT,
! 			 ControlFile->system_identifier);
  
  	printf(_("pg_control version number:            %u\n"),
! 		   ControlFile->pg_control_version);
  	printf(_("Catalog version number:               %u\n"),
! 		   ControlFile->catalog_version_no);
  	printf(_("Database system identifier:           %s\n"),
  		   sysident_str);
  	printf(_("Database cluster state:               %s\n"),
! 		   dbState(ControlFile->state));
  	printf(_("pg_control last modified:             %s\n"),
  		   pgctime_str);
  	printf(_("Latest checkpoint location:           %X/%X\n"),
! 		   (uint32) (ControlFile->checkPoint >> 32),
! 		   (uint32) ControlFile->checkPoint);
  	printf(_("Prior checkpoint location:            %X/%X\n"),
! 		   (uint32) (ControlFile->prevCheckPoint >> 32),
! 		   (uint32) ControlFile->prevCheckPoint);
  	printf(_("Latest checkpoint's REDO location:    %X/%X\n"),
! 		   (uint32) (ControlFile->checkPointCopy.redo >> 32),
! 		   (uint32) ControlFile->checkPointCopy.redo);
  	printf(_("Latest checkpoint's REDO WAL file:    %s\n"),
  		   xlogfilename);
  	printf(_("Latest checkpoint's TimeLineID:       %u\n"),
! 		   ControlFile->checkPointCopy.ThisTimeLineID);
  	printf(_("Latest checkpoint's PrevTimeLineID:   %u\n"),
! 		   ControlFile->checkPointCopy.PrevTimeLineID);
  	printf(_("Latest checkpoint's full_page_writes: %s\n"),
! 		   ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
  	printf(_("Latest checkpoint's NextXID:          %u:%u\n"),
! 		   ControlFile->checkPointCopy.nextXidEpoch,
! 		   ControlFile->checkPointCopy.nextXid);
  	printf(_("Latest checkpoint's NextOID:          %u\n"),
! 		   ControlFile->checkPointCopy.nextOid);
  	printf(_("Latest checkpoint's NextMultiXactId:  %u\n"),
! 		   ControlFile->checkPointCopy.nextMulti);
  	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
! 		   ControlFile->checkPointCopy.nextMultiOffset);
  	printf(_("Latest checkpoint's oldestXID:        %u\n"),
! 		   ControlFile->checkPointCopy.oldestXid);
  	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
! 		   ControlFile->checkPointCopy.oldestXidDB);
  	printf(_("Latest checkpoint's oldestActiveXID:  %u\n"),
! 		   ControlFile->checkPointCopy.oldestActiveXid);
  	printf(_("Latest checkpoint's oldestMultiXid:   %u\n"),
! 		   ControlFile->checkPointCopy.oldestMulti);
  	printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
! 		   ControlFile->checkPointCopy.oldestMultiDB);
  	printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
! 		   ControlFile->checkPointCopy.oldestCommitTsXid);
  	printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
! 		   ControlFile->checkPointCopy.newestCommitTsXid);
  	printf(_("Time of latest checkpoint:            %s\n"),
  		   ckpttime_str);
  	printf(_("Fake LSN counter for unlogged rels:   %X/%X\n"),
! 		   (uint32) (ControlFile->unloggedLSN >> 32),
! 		   (uint32) ControlFile->unloggedLSN);
  	printf(_("Minimum recovery ending location:     %X/%X\n"),
! 		   (uint32) (ControlFile->minRecoveryPoint >> 32),
! 		   (uint32) ControlFile->minRecoveryPoint);
  	printf(_("Min recovery ending loc's timeline:   %u\n"),
! 		   ControlFile->minRecoveryPointTLI);
  	printf(_("Backup start location:                %X/%X\n"),
! 		   (uint32) (ControlFile->backupStartPoint >> 32),
! 		   (uint32) ControlFile->backupStartPoint);
  	printf(_("Backup end location:                  %X/%X\n"),
! 		   (uint32) (ControlFile->backupEndPoint >> 32),
! 		   (uint32) ControlFile->backupEndPoint);
  	printf(_("End-of-backup record required:        %s\n"),
! 		   ControlFile->backupEndRequired ? _("yes") : _("no"));
  	printf(_("wal_level setting:                    %s\n"),
! 		   wal_level_str(ControlFile->wal_level));
  	printf(_("wal_log_hints setting:                %s\n"),
! 		   ControlFile->wal_log_hints ? _("on") : _("off"));
  	printf(_("max_connections setting:              %d\n"),
! 		   ControlFile->MaxConnections);
  	printf(_("max_worker_processes setting:         %d\n"),
! 		   ControlFile->max_worker_processes);
  	printf(_("max_prepared_xacts setting:           %d\n"),
! 		   ControlFile->max_prepared_xacts);
  	printf(_("max_locks_per_xact setting:           %d\n"),
! 		   ControlFile->max_locks_per_xact);
  	printf(_("track_commit_timestamp setting:       %s\n"),
! 		   ControlFile->track_commit_timestamp ? _("on") : _("off"));
  	printf(_("Maximum data alignment:               %u\n"),
! 		   ControlFile->maxAlign);
  	/* we don't print floatFormat since can't say much useful about it */
  	printf(_("Database block size:                  %u\n"),
! 		   ControlFile->blcksz);
  	printf(_("Blocks per segment of large relation: %u\n"),
! 		   ControlFile->relseg_size);
  	printf(_("WAL block size:                       %u\n"),
! 		   ControlFile->xlog_blcksz);
  	printf(_("Bytes per WAL segment:                %u\n"),
! 		   ControlFile->xlog_seg_size);
  	printf(_("Maximum length of identifiers:        %u\n"),
! 		   ControlFile->nameDataLen);
  	printf(_("Maximum columns in an index:          %u\n"),
! 		   ControlFile->indexMaxKeys);
  	printf(_("Maximum size of a TOAST chunk:        %u\n"),
! 		   ControlFile->toast_max_chunk_size);
  	printf(_("Size of a large-object chunk:         %u\n"),
! 		   ControlFile->loblksize);
  	printf(_("Date/time type storage:               %s\n"),
! 		   (ControlFile->enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
  	printf(_("Float4 argument passing:              %s\n"),
! 		   (ControlFile->float4ByVal ? _("by value") : _("by reference")));
  	printf(_("Float8 argument passing:              %s\n"),
! 		   (ControlFile->float8ByVal ? _("by value") : _("by reference")));
  	printf(_("Data page checksum version:           %u\n"),
! 		   ControlFile->data_checksum_version);
  	return 0;
  }
diff --git a/src/common/Makefile b/src/common/Makefile
index bde4fc2..f7a4a4d 100644
*** a/src/common/Makefile
--- b/src/common/Makefile
*************** override CPPFLAGS += -DVAL_LDFLAGS_EX="\
*** 36,43 ****
  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
  
--- 36,43 ----
  override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
  override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
  
! OBJS_COMMON = config_info.o controldata_utils.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
  
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index ...b6d0a12 .
*** a/src/common/controldata_utils.c
--- b/src/common/controldata_utils.c
***************
*** 0 ****
--- 1,100 ----
+ /*-------------------------------------------------------------------------
+  *
+  * controldata_utils.c
+  *		Common code for control data file output.
+  *
+  *
+  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/common/controldata_utils.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ #ifndef FRONTEND
+ #include "postgres.h"
+ #else
+ #include "postgres_fe.h"
+ #endif
+ 
+ #include <unistd.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ 
+ #include "catalog/pg_control.h"
+ #include "common/controldata_utils.h"
+ #include "port/pg_crc32c.h"
+ 
+ #ifndef FRONTEND
+ /* NOTE: caller must provide gettext call around the format string */
+ #define log_error(...)	\
+ 	elog(ERROR, __VA_ARGS__)
+ #else
+ #define log_error(...)	\
+ 	do { \
+ 			char *buf = psprintf(__VA_ARGS__); \
+ 			fprintf(stderr, "%s: %s\n", progname, buf); \
+ 			exit(2); \
+ 	} while (0)
+ #endif
+ 
+ /*
+  * get_controlfile(char *DataDir, const char *progname)
+  *
+  * Get controlfile values. The caller is responsible
+  * for pfreeing the result.
+  */
+ ControlFileData *
+ get_controlfile(char *DataDir, const char *progname)
+ {
+ 	ControlFileData	   *ControlFile;
+ 	int					fd;
+ 	char				ControlFilePath[MAXPGPATH];
+ 	pg_crc32c			crc;
+ 
+ 	ControlFile = palloc(sizeof(ControlFileData));
+ 	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
+ 
+ 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+ 		log_error(_("could not open file \"%s\" for reading: %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
+ 		log_error(_("could not read file \"%s\": %s"),
+ 				  ControlFilePath, strerror(errno));
+ 
+ 	close(fd);
+ 
+ 	/* Check the CRC. */
+ 	INIT_CRC32C(crc);
+ 	COMP_CRC32C(crc,
+ 			   (char *) ControlFile,
+ 			   offsetof(ControlFileData, crc));
+ 	FIN_CRC32C(crc);
+ 
+ 	if (!EQ_CRC32C(crc, ControlFile->crc))
+ #ifndef FRONTEND
+ 		elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
+ #else
+ 		printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
+ 				 "Either the file is corrupt, or it has a different layout than this program\n"
+ 				 "is expecting.  The results below are untrustworthy.\n\n"));
+ #endif
+ 
+ 	/* Make sure the control file is valid byte order. */
+ 	if (ControlFile->pg_control_version % 65536 == 0 &&
+ 		ControlFile->pg_control_version / 65536 != 0)
+ #ifndef FRONTEND
+ 		elog(ERROR, _("byte ordering mismatch"));
+ #else
+ 		printf(_("WARNING: possible byte ordering mismatch\n"
+ 				 "The byte ordering used to store the pg_control file might not match the one\n"
+ 				 "used by this program.  In that case the results below would be incorrect, and\n"
+ 				 "the PostgreSQL installation would be incompatible with this data directory.\n"));
+ #endif
+ 
+ 	return ControlFile;
+ }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 62b9125..e22bfd3 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("row security for current context
*** 5216,5221 ****
--- 5216,5234 ----
  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");
  
+ /* pg_controldata related functions */
+ DATA(insert OID = 3441 ( pg_control_system PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,20,1184}" "{o,o,o,o}" "{pg_control_version,catalog_version_no,system_identifier,pg_control_last_modified}" _null_ _null_ pg_control_system _null_ _null_ _null_ ));
+ DESCR("pg_controldata general state information as a function");
+ 
+ DATA(insert OID = 3442 ( pg_control_checkpoint PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,3220,3220,25,23,23,16,25,26,28,28,28,26,28,28,26,28,28,1184}" "{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}" "{checkpoint_location,prior_location,redo_location,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}" _null_ _null_ pg_control_checkpoint _null_ _null_ _null_ ));
+ DESCR("pg_controldata checkpoint state information as a function");
+ 
+ DATA(insert OID = 3443 ( pg_control_recovery PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{3220,23,3220,3220,16}" "{o,o,o,o,o}" "{min_recovery_end_location,min_recovery_end_timeline,backup_start_location,backup_end_location,end_of_backup_record_required}" _null_ _null_ pg_control_recovery _null_ _null_ _null_ ));
+ DESCR("pg_controldata recovery state information as a function");
+ 
+ DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
+ DESCR("pg_controldata init state information 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/controldata_utils.h b/src/include/common/controldata_utils.h
index ...1a09765 .
*** a/src/include/common/controldata_utils.h
--- b/src/include/common/controldata_utils.h
***************
*** 0 ****
--- 1,15 ----
+ /*
+  * controldata_utils.h
+  *		Common code for pg_controldata output
+  *
+  *	Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+  *	Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *	src/include/common/controldata_utils.h
+  */
+ #ifndef COMMON_CONTROLDATA_UTILS_H
+ #define COMMON_CONTROLDATA_UTILS_H
+ 
+ extern ControlFileData *get_controlfile(char *DataDir, const char *progname);
+ 
+ #endif   /* COMMON_CONTROLDATA_UTILS_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7ec93c9..115f8af 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum show_all_file_settings(PG_F
*** 1151,1156 ****
--- 1151,1162 ----
  /* pg_config.c */
  extern Datum pg_config(PG_FUNCTION_ARGS);
  
+ /* pg_controldata.c */
+ extern Datum pg_control_checkpoint(PG_FUNCTION_ARGS);
+ extern Datum pg_control_system(PG_FUNCTION_ARGS);
+ extern Datum pg_control_init(PG_FUNCTION_ARGS);
+ extern Datum pg_control_recovery(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/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4fb44e..949077a 100644
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
*************** sub mkvcbuild
*** 106,113 ****
  	}
  
  	our @pgcommonallfiles = qw(
! 	  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
--- 106,113 ----
  	}
  
  	our @pgcommonallfiles = qw(
! 	  config_info.c controldata_utils.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
#105Michael Paquier
michael.paquier@gmail.com
In reply to: Joe Conway (#104)
Re: exposing pg_controldata and pg_config as functions

On Mon, Feb 29, 2016 at 3:50 AM, Joe Conway <mail@joeconway.com> wrote:

If there are no other complaints or comments, I will commit the attached
sometime this coming week (the the requisite catversion bump).

Thanks for the updated patch, I have nothing else to say on my side.
The new version has fixed the MSVC build, I just double-checked it.
--
Michael

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#106Joe Conway
mail@joeconway.com
In reply to: Michael Paquier (#105)
Re: exposing pg_controldata and pg_config as functions

On 02/28/2016 07:45 PM, Michael Paquier wrote:

On Mon, Feb 29, 2016 at 3:50 AM, Joe Conway <mail@joeconway.com> wrote:

If there are no other complaints or comments, I will commit the attached
sometime this coming week (the the requisite catversion bump).

Thanks for the updated patch, I have nothing else to say on my side.
The new version has fixed the MSVC build, I just double-checked it.

Pushed with catversion bump.

Thanks,

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development