mirror of https://github.com/postgres/postgres
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.pull/11/head
parent
d34794f7d5
commit
dc7d70ea05
@ -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)); |
||||
} |
@ -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; |
||||
} |
@ -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 */ |
Loading…
Reference in new issue