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 <m...@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
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 = (

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to