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
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 = (

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to