Expose control file data via SQL accessible functions.

Add four new SQL accessible functions: pg_control_system(),
pg_control_checkpoint(), pg_control_recovery(), and pg_control_init()
which expose a subset of the control file data.

Along the way move the code to read and validate the control file to
src/common, where it can be shared by the new backend functions
and the original pg_controldata frontend program.

Patch by me, significant input, testing, and review by Michael Paquier.
This commit is contained in:
Joe Conway 2016-03-05 11:10:19 -08:00
parent d34794f7d5
commit dc7d70ea05
11 changed files with 900 additions and 105 deletions

View File

@ -16703,6 +16703,362 @@ SELECT collation for ('foo' COLLATE "de_DE");
</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">

View File

@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_config.o pg_rusage.o \
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

View File

@ -0,0 +1,341 @@
/*-------------------------------------------------------------------------
*
* pg_controldata.c
*
* Routines to expose the contents of the control data file via
* a set of SQL functions.
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/utils/misc/pg_controldata.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "access/htup_details.h"
#include "access/xlog_internal.h"
#include "catalog/pg_control.h"
#include "catalog/pg_type.h"
#include "common/controldata_utils.h"
#include "utils/builtins.h"
#include "utils/pg_lsn.h"
#include "utils/timestamp.h"
Datum
pg_control_system(PG_FUNCTION_ARGS)
{
Datum values[4];
bool nulls[4];
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
/*
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
tupdesc = CreateTemplateTupleDesc(4, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pg_control_version",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catalog_version_no",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "system_identifier",
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pg_control_last_modified",
TIMESTAMPTZOID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
/* read the control file */
ControlFile = get_controlfile(DataDir, NULL);
values[0] = Int32GetDatum(ControlFile->pg_control_version);
nulls[0] = false;
values[1] = Int32GetDatum(ControlFile->catalog_version_no);
nulls[1] = false;
values[2] = Int64GetDatum(ControlFile->system_identifier);
nulls[2] = false;
values[3] = TimestampTzGetDatum(time_t_to_timestamptz(ControlFile->time));
nulls[3] = false;
htup = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}
Datum
pg_control_checkpoint(PG_FUNCTION_ARGS)
{
Datum values[19];
bool nulls[19];
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
XLogSegNo segno;
char xlogfilename[MAXFNAMELEN];
/*
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
tupdesc = CreateTemplateTupleDesc(19, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "checkpoint_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "prior_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "redo_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "redo_wal_file",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "timeline_id",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "prev_timeline_id",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "full_page_writes",
BOOLOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_xid",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_oid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multixact_id",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 11, "next_multi_offset",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_xid_dbid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_active_xid",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_xid",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_multi_dbid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 17, "oldest_commit_ts_xid",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 18, "newest_commit_ts_xid",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 19, "checkpoint_time",
TIMESTAMPTZOID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
/* Read the control file. */
ControlFile = get_controlfile(DataDir, NULL);
/*
* Calculate name of the WAL file containing the latest checkpoint's REDO
* start point.
*/
XLByteToSeg(ControlFile->checkPointCopy.redo, segno);
XLogFileName(xlogfilename, ControlFile->checkPointCopy.ThisTimeLineID, segno);
/* Populate the values and null arrays */
values[0] = LSNGetDatum(ControlFile->checkPoint);
nulls[0] = false;
values[1] = LSNGetDatum(ControlFile->prevCheckPoint);
nulls[1] = false;
values[2] = LSNGetDatum(ControlFile->checkPointCopy.redo);
nulls[2] = false;
values[3] = CStringGetTextDatum(xlogfilename);
nulls[3] = false;
values[4] = Int32GetDatum(ControlFile->checkPointCopy.ThisTimeLineID);
nulls[4] = false;
values[5] = Int32GetDatum(ControlFile->checkPointCopy.PrevTimeLineID);
nulls[5] = false;
values[6] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
nulls[6] = false;
values[7] = CStringGetTextDatum(psprintf("%u:%u",
ControlFile->checkPointCopy.nextXidEpoch,
ControlFile->checkPointCopy.nextXid));
nulls[7] = false;
values[8] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
nulls[8] = false;
values[9] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMulti);
nulls[9] = false;
values[10] = TransactionIdGetDatum(ControlFile->checkPointCopy.nextMultiOffset);
nulls[10] = false;
values[11] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestXid);
nulls[11] = false;
values[12] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestXidDB);
nulls[12] = false;
values[13] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestActiveXid);
nulls[13] = false;
values[14] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestMulti);
nulls[14] = false;
values[15] = ObjectIdGetDatum(ControlFile->checkPointCopy.oldestMultiDB);
nulls[15] = false;
values[16] = TransactionIdGetDatum(ControlFile->checkPointCopy.oldestCommitTsXid);
nulls[16] = false;
values[17] = TransactionIdGetDatum(ControlFile->checkPointCopy.newestCommitTsXid);
nulls[17] = false;
values[18] = TimestampTzGetDatum(
time_t_to_timestamptz(ControlFile->checkPointCopy.time));
nulls[18] = false;
htup = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}
Datum
pg_control_recovery(PG_FUNCTION_ARGS)
{
Datum values[5];
bool nulls[5];
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
/*
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
tupdesc = CreateTemplateTupleDesc(5, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "min_recovery_end_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "min_recovery_end_timeline",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backup_start_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "backup_end_location",
LSNOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "end_of_backup_record_required",
BOOLOID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
/* read the control file */
ControlFile = get_controlfile(DataDir, NULL);
values[0] = LSNGetDatum(ControlFile->minRecoveryPoint);
nulls[0] = false;
values[1] = Int32GetDatum(ControlFile->minRecoveryPointTLI);
nulls[1] = false;
values[2] = LSNGetDatum(ControlFile->backupStartPoint);
nulls[2] = false;
values[3] = LSNGetDatum(ControlFile->backupEndPoint);
nulls[3] = false;
values[4] = BoolGetDatum(ControlFile->backupEndRequired);
nulls[4] = false;
htup = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}
Datum
pg_control_init(PG_FUNCTION_ARGS)
{
Datum values[13];
bool nulls[13];
TupleDesc tupdesc;
HeapTuple htup;
ControlFileData *ControlFile;
/*
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
tupdesc = CreateTemplateTupleDesc(13, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "blocks_per_segment",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "wal_block_size",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bytes_per_wal_segment",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "max_identifier_length",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "max_index_columns",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "max_toast_chunk_size",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "large_object_chunk_size",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "bigint_timestamps",
BOOLOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 11, "float4_pass_by_value",
BOOLOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 12, "float8_pass_by_value",
BOOLOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 13, "data_page_checksum_version",
INT4OID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
/* read the control file */
ControlFile = get_controlfile(DataDir, NULL);
values[0] = Int32GetDatum(ControlFile->maxAlign);
nulls[0] = false;
values[1] = Int32GetDatum(ControlFile->blcksz);
nulls[1] = false;
values[2] = Int32GetDatum(ControlFile->relseg_size);
nulls[2] = false;
values[3] = Int32GetDatum(ControlFile->xlog_blcksz);
nulls[3] = false;
values[4] = Int32GetDatum(ControlFile->xlog_seg_size);
nulls[4] = false;
values[5] = Int32GetDatum(ControlFile->nameDataLen);
nulls[5] = false;
values[6] = Int32GetDatum(ControlFile->indexMaxKeys);
nulls[6] = false;
values[7] = Int32GetDatum(ControlFile->toast_max_chunk_size);
nulls[7] = false;
values[8] = Int32GetDatum(ControlFile->loblksize);
nulls[8] = false;
values[9] = BoolGetDatum(ControlFile->enableIntTimes);
nulls[9] = false;
values[10] = BoolGetDatum(ControlFile->float4ByVal);
nulls[10] = false;
values[11] = BoolGetDatum(ControlFile->float8ByVal);
nulls[11] = false;
values[12] = Int32GetDatum(ControlFile->data_checksum_version);
nulls[12] = false;
htup = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(htup));
}

View File

@ -18,14 +18,12 @@
#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 "common/controldata_utils.h"
#include "pg_getopt.h"
@ -89,11 +87,8 @@ wal_level_str(WalLevel wal_level)
int
main(int argc, char *argv[])
{
ControlFileData ControlFile;
int fd;
char ControlFilePath[MAXPGPATH];
ControlFileData *ControlFile;
char *DataDir = NULL;
pg_crc32c crc;
time_t time_tmp;
char pgctime_str[128];
char ckpttime_str[128];
@ -161,34 +156,8 @@ main(int argc, char *argv[])
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"));
/* get a copy of the control file */
ControlFile = get_controlfile(DataDir, progname);
/*
* This slightly-chintzy coding will work as long as the control file
@ -199,10 +168,10 @@ main(int argc, char *argv[])
* Use variable for format to suppress overly-anal-retentive gcc warning
* about %c
*/
time_tmp = (time_t) ControlFile.time;
time_tmp = (time_t) ControlFile->time;
strftime(pgctime_str, sizeof(pgctime_str), strftime_fmt,
localtime(&time_tmp));
time_tmp = (time_t) ControlFile.checkPointCopy.time;
time_tmp = (time_t) ControlFile->checkPointCopy.time;
strftime(ckpttime_str, sizeof(ckpttime_str), strftime_fmt,
localtime(&time_tmp));
@ -210,129 +179,124 @@ main(int argc, char *argv[])
* 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);
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);
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"));
ControlFile->pg_control_version);
printf(_("Catalog version number: %u\n"),
ControlFile.catalog_version_no);
ControlFile->catalog_version_no);
printf(_("Database system identifier: %s\n"),
sysident_str);
printf(_("Database cluster state: %s\n"),
dbState(ControlFile.state));
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);
(uint32) (ControlFile->checkPoint >> 32),
(uint32) ControlFile->checkPoint);
printf(_("Prior checkpoint location: %X/%X\n"),
(uint32) (ControlFile.prevCheckPoint >> 32),
(uint32) ControlFile.prevCheckPoint);
(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);
(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);
ControlFile->checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's PrevTimeLineID: %u\n"),
ControlFile.checkPointCopy.PrevTimeLineID);
ControlFile->checkPointCopy.PrevTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
printf(_("Latest checkpoint's NextXID: %u:%u\n"),
ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid);
ControlFile->checkPointCopy.nextXidEpoch,
ControlFile->checkPointCopy.nextXid);
printf(_("Latest checkpoint's NextOID: %u\n"),
ControlFile.checkPointCopy.nextOid);
ControlFile->checkPointCopy.nextOid);
printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
ControlFile.checkPointCopy.nextMulti);
ControlFile->checkPointCopy.nextMulti);
printf(_("Latest checkpoint's NextMultiOffset: %u\n"),
ControlFile.checkPointCopy.nextMultiOffset);
ControlFile->checkPointCopy.nextMultiOffset);
printf(_("Latest checkpoint's oldestXID: %u\n"),
ControlFile.checkPointCopy.oldestXid);
ControlFile->checkPointCopy.oldestXid);
printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
ControlFile.checkPointCopy.oldestXidDB);
ControlFile->checkPointCopy.oldestXidDB);
printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
ControlFile.checkPointCopy.oldestActiveXid);
ControlFile->checkPointCopy.oldestActiveXid);
printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
ControlFile.checkPointCopy.oldestMulti);
ControlFile->checkPointCopy.oldestMulti);
printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
ControlFile.checkPointCopy.oldestMultiDB);
ControlFile->checkPointCopy.oldestMultiDB);
printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
ControlFile.checkPointCopy.oldestCommitTsXid);
ControlFile->checkPointCopy.oldestCommitTsXid);
printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
ControlFile.checkPointCopy.newestCommitTsXid);
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);
(uint32) (ControlFile->unloggedLSN >> 32),
(uint32) ControlFile->unloggedLSN);
printf(_("Minimum recovery ending location: %X/%X\n"),
(uint32) (ControlFile.minRecoveryPoint >> 32),
(uint32) ControlFile.minRecoveryPoint);
(uint32) (ControlFile->minRecoveryPoint >> 32),
(uint32) ControlFile->minRecoveryPoint);
printf(_("Min recovery ending loc's timeline: %u\n"),
ControlFile.minRecoveryPointTLI);
ControlFile->minRecoveryPointTLI);
printf(_("Backup start location: %X/%X\n"),
(uint32) (ControlFile.backupStartPoint >> 32),
(uint32) ControlFile.backupStartPoint);
(uint32) (ControlFile->backupStartPoint >> 32),
(uint32) ControlFile->backupStartPoint);
printf(_("Backup end location: %X/%X\n"),
(uint32) (ControlFile.backupEndPoint >> 32),
(uint32) ControlFile.backupEndPoint);
(uint32) (ControlFile->backupEndPoint >> 32),
(uint32) ControlFile->backupEndPoint);
printf(_("End-of-backup record required: %s\n"),
ControlFile.backupEndRequired ? _("yes") : _("no"));
ControlFile->backupEndRequired ? _("yes") : _("no"));
printf(_("wal_level setting: %s\n"),
wal_level_str(ControlFile.wal_level));
wal_level_str(ControlFile->wal_level));
printf(_("wal_log_hints setting: %s\n"),
ControlFile.wal_log_hints ? _("on") : _("off"));
ControlFile->wal_log_hints ? _("on") : _("off"));
printf(_("max_connections setting: %d\n"),
ControlFile.MaxConnections);
ControlFile->MaxConnections);
printf(_("max_worker_processes setting: %d\n"),
ControlFile.max_worker_processes);
ControlFile->max_worker_processes);
printf(_("max_prepared_xacts setting: %d\n"),
ControlFile.max_prepared_xacts);
ControlFile->max_prepared_xacts);
printf(_("max_locks_per_xact setting: %d\n"),
ControlFile.max_locks_per_xact);
ControlFile->max_locks_per_xact);
printf(_("track_commit_timestamp setting: %s\n"),
ControlFile.track_commit_timestamp ? _("on") : _("off"));
ControlFile->track_commit_timestamp ? _("on") : _("off"));
printf(_("Maximum data alignment: %u\n"),
ControlFile.maxAlign);
ControlFile->maxAlign);
/* we don't print floatFormat since can't say much useful about it */
printf(_("Database block size: %u\n"),
ControlFile.blcksz);
ControlFile->blcksz);
printf(_("Blocks per segment of large relation: %u\n"),
ControlFile.relseg_size);
ControlFile->relseg_size);
printf(_("WAL block size: %u\n"),
ControlFile.xlog_blcksz);
ControlFile->xlog_blcksz);
printf(_("Bytes per WAL segment: %u\n"),
ControlFile.xlog_seg_size);
ControlFile->xlog_seg_size);
printf(_("Maximum length of identifiers: %u\n"),
ControlFile.nameDataLen);
ControlFile->nameDataLen);
printf(_("Maximum columns in an index: %u\n"),
ControlFile.indexMaxKeys);
ControlFile->indexMaxKeys);
printf(_("Maximum size of a TOAST chunk: %u\n"),
ControlFile.toast_max_chunk_size);
ControlFile->toast_max_chunk_size);
printf(_("Size of a large-object chunk: %u\n"),
ControlFile.loblksize);
ControlFile->loblksize);
printf(_("Date/time type storage: %s\n"),
(ControlFile.enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
(ControlFile->enableIntTimes ? _("64-bit integers") : _("floating-point numbers")));
printf(_("Float4 argument passing: %s\n"),
(ControlFile.float4ByVal ? _("by value") : _("by reference")));
(ControlFile->float4ByVal ? _("by value") : _("by reference")));
printf(_("Float8 argument passing: %s\n"),
(ControlFile.float8ByVal ? _("by value") : _("by reference")));
(ControlFile->float8ByVal ? _("by value") : _("by reference")));
printf(_("Data page checksum version: %u\n"),
ControlFile.data_checksum_version);
ControlFile->data_checksum_version);
return 0;
}

View File

@ -36,8 +36,8 @@ override CPPFLAGS += -DVAL_LDFLAGS_EX="\"$(LDFLAGS_EX)\""
override CPPFLAGS += -DVAL_LDFLAGS_SL="\"$(LDFLAGS_SL)\""
override CPPFLAGS += -DVAL_LIBS="\"$(LIBS)\""
OBJS_COMMON = config_info.o exec.o pg_lzcompress.o pgfnames.o psprintf.o \
relpath.o rmtree.o string.o username.o wait_error.o
OBJS_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

View File

@ -0,0 +1,100 @@
/*-------------------------------------------------------------------------
*
* controldata_utils.c
* Common code for control data file output.
*
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/common/controldata_utils.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "catalog/pg_control.h"
#include "common/controldata_utils.h"
#include "port/pg_crc32c.h"
#ifndef FRONTEND
/* NOTE: caller must provide gettext call around the format string */
#define log_error(...) \
elog(ERROR, __VA_ARGS__)
#else
#define log_error(...) \
do { \
char *buf = psprintf(__VA_ARGS__); \
fprintf(stderr, "%s: %s\n", progname, buf); \
exit(2); \
} while (0)
#endif
/*
* get_controlfile(char *DataDir, const char *progname)
*
* Get controlfile values. The caller is responsible
* for pfreeing the result.
*/
ControlFileData *
get_controlfile(char *DataDir, const char *progname)
{
ControlFileData *ControlFile;
int fd;
char ControlFilePath[MAXPGPATH];
pg_crc32c crc;
ControlFile = palloc(sizeof(ControlFileData));
snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
log_error(_("could not open file \"%s\" for reading: %s"),
ControlFilePath, strerror(errno));
if (read(fd, ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
log_error(_("could not read file \"%s\": %s"),
ControlFilePath, strerror(errno));
close(fd);
/* Check the CRC. */
INIT_CRC32C(crc);
COMP_CRC32C(crc,
(char *) ControlFile,
offsetof(ControlFileData, crc));
FIN_CRC32C(crc);
if (!EQ_CRC32C(crc, ControlFile->crc))
#ifndef FRONTEND
elog(ERROR, _("calculated CRC checksum does not match value stored in file"));
#else
printf(_("WARNING: Calculated CRC checksum does not match value stored in file.\n"
"Either the file is corrupt, or it has a different layout than this program\n"
"is expecting. The results below are untrustworthy.\n\n"));
#endif
/* Make sure the control file is valid byte order. */
if (ControlFile->pg_control_version % 65536 == 0 &&
ControlFile->pg_control_version / 65536 != 0)
#ifndef FRONTEND
elog(ERROR, _("byte ordering mismatch"));
#else
printf(_("WARNING: possible byte ordering mismatch\n"
"The byte ordering used to store the pg_control file might not match the one\n"
"used by this program. In that case the results below would be incorrect, and\n"
"the PostgreSQL installation would be incompatible with this data directory.\n"));
#endif
return ControlFile;
}

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201603021
#define CATALOG_VERSION_NO 201603051
#endif

View File

@ -5225,6 +5225,19 @@ DESCR("row security for current context active on table by table name");
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,

View File

@ -0,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 */

View File

@ -1151,6 +1151,12 @@ extern Datum show_all_file_settings(PG_FUNCTION_ARGS);
/* pg_config.c */
extern Datum pg_config(PG_FUNCTION_ARGS);
/* pg_controldata.c */
extern Datum pg_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);

View File

@ -106,8 +106,8 @@ sub mkvcbuild
}
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);
config_info.c controldata_utils.c exec.c pg_lzcompress.c pgfnames.c
psprintf.c relpath.c rmtree.c string.c username.c wait_error.c);
our @pgcommonfrontendfiles = (
@pgcommonallfiles, qw(fe_memutils.c