Move pg_controldata to /bin.

This commit is contained in:
Bruce Momjian 2002-08-17 02:43:08 +00:00
parent a208ea72bc
commit 6945ea3445
5 changed files with 5 additions and 676 deletions

View File

@ -1,19 +0,0 @@
# $Header: /cvsroot/pgsql/contrib/pg_resetxlog/Attic/Makefile,v 1.6 2002/07/27 20:10:03 petere Exp $
subdir = contrib/pg_resetxlog
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
all: submake-libpgport
PROGRAM = pg_resetxlog
OBJS = pg_resetxlog.o pg_crc.o
pg_crc.c: $(top_srcdir)/src/backend/utils/hash/pg_crc.c
rm -f $@ && $(LN_S) $< .
EXTRA_CLEAN = pg_crc.c
DOCS = README.pg_resetxlog
include $(top_srcdir)/contrib/contrib-global.mk

View File

@ -1,42 +0,0 @@
pg_resetxlog is a program to clear the WAL transaction log (stored in
$PGDATA/pg_xlog/), replacing whatever had been in it with just a dummy
shutdown-checkpoint record. It also regenerates the pg_control file
if necessary.
THIS PROGRAM WILL DESTROY VALUABLE LOG DATA!!! Don't run it unless you
really need it!!!
pg_resetxlog is primarily intended for disaster recovery --- that is,
if your pg_control and/or xlog are hosed badly enough that Postgres refuses
to start up, this program will get you past that problem and let you get to
your data files. But realize that without the xlog, your data files may be
corrupt due to partially-applied transactions, incomplete index-file
updates, etc. You should dump your data, check it for accuracy, then initdb
and reload.
A secondary purpose is to cope with xlog format changes without requiring
initdb. To use pg_resetxlog for this purpose, just be sure that you have
cleanly shut down your old postmaster (if you're not sure, see the contrib
module pg_controldata and run it to be sure the DB state is SHUTDOWN).
Then run pg_resetxlog, and finally install and start the new version of
the database software.
A tertiary purpose is its use by pg_upgrade to set the next transaction
id and checkpoint location in pg_control.
To run the program, make sure your postmaster is not running, then
(as the Postgres admin user) do
pg_resetxlog $PGDATA
As a safety measure, the target data directory must be specified on the
command line, it cannot be defaulted.
If pg_resetxlog complains that it can't reconstruct valid data for pg_control,
you can force it to invent plausible data values with
pg_resetxlog -f $PGDATA
If this turns out to be necessary then you *definitely* should plan on
immediate dump, initdb, reload --- any modifications you do to the database
after "pg_resetxlog -f" would be likely to corrupt things even worse.

View File

@ -1,611 +0,0 @@
/*-------------------------------------------------------------------------
*
* pg_resetxlog.c
* A utility to "zero out" the xlog when it's corrupt beyond recovery.
* Can also rebuild pg_control if needed.
*
* The theory of operation is fairly simple:
* 1. Read the existing pg_control (which will include the last
* checkpoint record). If it is an old format then update to
* current format.
* 2. If pg_control is corrupt, attempt to intuit reasonable values,
* by scanning the old xlog if necessary.
* 3. Modify pg_control to reflect a "shutdown" state with a checkpoint
* record at the start of xlog.
* 4. Flush the existing xlog files and write a new segment with
* just a checkpoint record in it. The new segment is positioned
* just past the end of the old xlog, so that existing LSNs in
* data pages will appear to be "in the past".
* This is all pretty straightforward except for the intuition part of
* step 2 ...
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/contrib/pg_resetxlog/Attic/pg_resetxlog.c,v 1.19 2002/08/15 02:58:29 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef USE_LOCALE
#include <locale.h>
#endif
#include "access/xlog.h"
#include "catalog/catversion.h"
#include "catalog/pg_control.h"
/******************** stuff copied from xlog.c ********************/
/* Increment an xlogid/segment pair */
#define NextLogSeg(logId, logSeg) \
do { \
if ((logSeg) >= XLogSegsPerFile-1) \
{ \
(logId)++; \
(logSeg) = 0; \
} \
else \
(logSeg)++; \
} while (0)
#define XLogFileName(path, log, seg) \
snprintf(path, MAXPGPATH, "%s/%08X%08X", \
XLogDir, log, seg)
/******************** end of stuff copied from xlog.c ********************/
static char *DataDir; /* locations of important stuff */
static char XLogDir[MAXPGPATH];
static char ControlFilePath[MAXPGPATH];
static ControlFileData ControlFile; /* pg_control values */
static uint32 newXlogId,
newXlogSeg; /* ID/Segment of new XLOG segment */
static bool guessed = false; /* T if we had to guess at any values */
/*
* Try to read the existing pg_control file.
*
* This routine is also responsible for updating old pg_control versions
* to the current format.
*/
static bool
ReadControlFile(void)
{
int fd;
int len;
char *buffer;
crc64 crc;
if ((fd = open(ControlFilePath, O_RDONLY)) < 0)
{
/*
* If pg_control is not there at all, or we can't read it, the
* odds are we've been handed a bad DataDir path, so give up. User
* can do "touch pg_control" to force us to proceed.
*/
perror("Failed to open $PGDATA/global/pg_control for reading");
if (errno == ENOENT)
fprintf(stderr, "If you're sure the PGDATA path is correct, do\n"
" touch %s\n"
"and try again.\n", ControlFilePath);
exit(1);
}
/* Use malloc to ensure we have a maxaligned buffer */
buffer = (char *) malloc(BLCKSZ);
len = read(fd, buffer, BLCKSZ);
if (len < 0)
{
perror("Failed to read $PGDATA/global/pg_control");
exit(1);
}
close(fd);
if (len >= sizeof(ControlFileData) &&
((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION)
{
/* Check the CRC. */
INIT_CRC64(crc);
COMP_CRC64(crc,
buffer + sizeof(crc64),
sizeof(ControlFileData) - sizeof(crc64));
FIN_CRC64(crc);
if (EQ_CRC64(crc, ((ControlFileData *) buffer)->crc))
{
/* Valid data... */
memcpy(&ControlFile, buffer, sizeof(ControlFile));
return true;
}
fprintf(stderr, "pg_control exists but has invalid CRC; proceed with caution.\n");
/* We will use the data anyway, but treat it as guessed. */
memcpy(&ControlFile, buffer, sizeof(ControlFile));
guessed = true;
return true;
}
/* Looks like it's a mess. */
fprintf(stderr, "pg_control exists but is broken or unknown version; ignoring it.\n");
return false;
}
/*
* Guess at pg_control values when we can't read the old ones.
*/
static void
GuessControlValues(void)
{
#ifdef USE_LOCALE
char *localeptr;
#endif
/*
* Set up a completely default set of pg_control values.
*/
guessed = true;
memset(&ControlFile, 0, sizeof(ControlFile));
ControlFile.pg_control_version = PG_CONTROL_VERSION;
ControlFile.catalog_version_no = CATALOG_VERSION_NO;
ControlFile.checkPointCopy.redo.xlogid = 0;
ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogPHD;
ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
ControlFile.checkPointCopy.ThisStartUpID = 0;
ControlFile.checkPointCopy.nextXid = (TransactionId) 514; /* XXX */
ControlFile.checkPointCopy.nextOid = BootstrapObjectIdData;
ControlFile.checkPointCopy.time = time(NULL);
ControlFile.state = DB_SHUTDOWNED;
ControlFile.time = time(NULL);
ControlFile.logId = 0;
ControlFile.logSeg = 1;
ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
ControlFile.blcksz = BLCKSZ;
ControlFile.relseg_size = RELSEG_SIZE;
#ifdef USE_LOCALE
localeptr = setlocale(LC_COLLATE, "");
if (!localeptr)
{
fprintf(stderr, "Invalid LC_COLLATE setting\n");
exit(1);
}
StrNCpy(ControlFile.lc_collate, localeptr, LOCALE_NAME_BUFLEN);
localeptr = setlocale(LC_CTYPE, "");
if (!localeptr)
{
fprintf(stderr, "Invalid LC_CTYPE setting\n");
exit(1);
}
StrNCpy(ControlFile.lc_ctype, localeptr, LOCALE_NAME_BUFLEN);
#else
strcpy(ControlFile.lc_collate, "C");
strcpy(ControlFile.lc_ctype, "C");
#endif
/*
* XXX eventually, should try to grovel through old XLOG to develop
* more accurate values for startupid, nextXID, and nextOID.
*/
}
/*
* Print the guessed pg_control values when we had to guess.
*
* NB: this display should be just those fields that will not be
* reset by RewriteControlFile().
*/
static void
PrintControlValues(bool guessed)
{
printf("%spg_control values:\n\n"
"pg_control version number: %u\n"
"Catalog version number: %u\n"
"Current log file id: %u\n"
"Next log file segment: %u\n"
"Latest checkpoint's StartUpID: %u\n"
"Latest checkpoint's NextXID: %u\n"
"Latest checkpoint's NextOID: %u\n"
"Database block size: %u\n"
"Blocks per segment of large relation: %u\n"
"LC_COLLATE: %s\n"
"LC_CTYPE: %s\n",
(guessed ? "Guessed-at " : ""),
ControlFile.pg_control_version,
ControlFile.catalog_version_no,
ControlFile.logId,
ControlFile.logSeg,
ControlFile.checkPointCopy.ThisStartUpID,
ControlFile.checkPointCopy.nextXid,
ControlFile.checkPointCopy.nextOid,
ControlFile.blcksz,
ControlFile.relseg_size,
ControlFile.lc_collate,
ControlFile.lc_ctype);
}
/*
* Write out the new pg_control file.
*/
static void
RewriteControlFile(void)
{
int fd;
char buffer[BLCKSZ]; /* need not be aligned */
/*
* Adjust fields as needed to force an empty XLOG starting at the next
* available segment.
*/
newXlogId = ControlFile.logId;
newXlogSeg = ControlFile.logSeg;
/* be sure we wrap around correctly at end of a logfile */
NextLogSeg(newXlogId, newXlogSeg);
ControlFile.checkPointCopy.redo.xlogid = newXlogId;
ControlFile.checkPointCopy.redo.xrecoff =
newXlogSeg * XLogSegSize + SizeOfXLogPHD;
ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
ControlFile.checkPointCopy.time = time(NULL);
ControlFile.state = DB_SHUTDOWNED;
ControlFile.time = time(NULL);
ControlFile.logId = newXlogId;
ControlFile.logSeg = newXlogSeg + 1;
ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
ControlFile.prevCheckPoint.xlogid = 0;
ControlFile.prevCheckPoint.xrecoff = 0;
/* Contents are protected with a CRC */
INIT_CRC64(ControlFile.crc);
COMP_CRC64(ControlFile.crc,
(char *) &ControlFile + sizeof(crc64),
sizeof(ControlFileData) - sizeof(crc64));
FIN_CRC64(ControlFile.crc);
/*
* We write out BLCKSZ bytes into pg_control, zero-padding the excess
* over sizeof(ControlFileData). This reduces the odds of
* premature-EOF errors when reading pg_control. We'll still fail
* when we check the contents of the file, but hopefully with a more
* specific error than "couldn't read pg_control".
*/
if (sizeof(ControlFileData) > BLCKSZ)
{
fprintf(stderr, "sizeof(ControlFileData) is too large ... fix xlog.c\n");
exit(1);
}
memset(buffer, 0, BLCKSZ);
memcpy(buffer, &ControlFile, sizeof(ControlFileData));
unlink(ControlFilePath);
fd = open(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0)
{
perror("RewriteControlFile failed to create pg_control file");
exit(1);
}
errno = 0;
if (write(fd, buffer, BLCKSZ) != BLCKSZ)
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
perror("RewriteControlFile failed to write pg_control file");
exit(1);
}
if (fsync(fd) != 0)
{
perror("fsync");
exit(1);
}
close(fd);
}
/*
* Remove existing XLOG files
*/
static void
KillExistingXLOG(void)
{
DIR *xldir;
struct dirent *xlde;
char path[MAXPGPATH];
xldir = opendir(XLogDir);
if (xldir == NULL)
{
perror("KillExistingXLOG: cannot open $PGDATA/pg_xlog directory");
exit(1);
}
errno = 0;
while ((xlde = readdir(xldir)) != NULL)
{
if (strlen(xlde->d_name) == 16 &&
strspn(xlde->d_name, "0123456789ABCDEF") == 16)
{
snprintf(path, MAXPGPATH, "%s/%s", XLogDir, xlde->d_name);
if (unlink(path) < 0)
{
perror(path);
exit(1);
}
}
errno = 0;
}
if (errno)
{
perror("KillExistingXLOG: cannot read $PGDATA/pg_xlog directory");
exit(1);
}
closedir(xldir);
}
/*
* Write an empty XLOG file, containing only the checkpoint record
* already set up in ControlFile.
*/
static void
WriteEmptyXLOG(void)
{
char *buffer;
XLogPageHeader page;
XLogRecord *record;
crc64 crc;
char path[MAXPGPATH];
int fd;
int nbytes;
/* Use malloc() to ensure buffer is MAXALIGNED */
buffer = (char *) malloc(BLCKSZ);
page = (XLogPageHeader) buffer;
/* Set up the first page with initial record */
memset(buffer, 0, BLCKSZ);
page->xlp_magic = XLOG_PAGE_MAGIC;
page->xlp_info = 0;
page->xlp_sui = ControlFile.checkPointCopy.ThisStartUpID;
page->xlp_pageaddr.xlogid =
ControlFile.checkPointCopy.redo.xlogid;
page->xlp_pageaddr.xrecoff =
ControlFile.checkPointCopy.redo.xrecoff - SizeOfXLogPHD;
record = (XLogRecord *) ((char *) page + SizeOfXLogPHD);
record->xl_prev.xlogid = 0;
record->xl_prev.xrecoff = 0;
record->xl_xact_prev = record->xl_prev;
record->xl_xid = InvalidTransactionId;
record->xl_len = sizeof(CheckPoint);
record->xl_info = XLOG_CHECKPOINT_SHUTDOWN;
record->xl_rmid = RM_XLOG_ID;
memcpy(XLogRecGetData(record), &ControlFile.checkPointCopy,
sizeof(CheckPoint));
INIT_CRC64(crc);
COMP_CRC64(crc, &ControlFile.checkPointCopy, sizeof(CheckPoint));
COMP_CRC64(crc, (char *) record + sizeof(crc64),
SizeOfXLogRecord - sizeof(crc64));
FIN_CRC64(crc);
record->xl_crc = crc;
/* Write the first page */
XLogFileName(path, newXlogId, newXlogSeg);
unlink(path);
fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
S_IRUSR | S_IWUSR);
if (fd < 0)
{
perror(path);
exit(1);
}
errno = 0;
if (write(fd, buffer, BLCKSZ) != BLCKSZ)
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
perror("WriteEmptyXLOG: failed to write xlog file");
exit(1);
}
/* Fill the rest of the file with zeroes */
memset(buffer, 0, BLCKSZ);
for (nbytes = BLCKSZ; nbytes < XLogSegSize; nbytes += BLCKSZ)
{
errno = 0;
if (write(fd, buffer, BLCKSZ) != BLCKSZ)
{
if (errno == 0)
errno = ENOSPC;
perror("WriteEmptyXLOG: failed to write xlog file");
exit(1);
}
}
if (fsync(fd) != 0)
{
perror("fsync");
exit(1);
}
close(fd);
}
static void
usage(void)
{
fprintf(stderr, "Usage: pg_resetxlog [-f] [-n] [-x xid] [ -l fileid seg ] PGDataDirectory\n"
" -f\t\tforce update to be done\n"
" -n\t\tno update, just show extracted pg_control values (for testing)\n"
" -x xid\tset next transaction ID\n"
" -l fileid seg\tforce minimum WAL starting location for new xlog\n");
exit(1);
}
int
main(int argc, char **argv)
{
int argn;
bool force = false;
bool noupdate = false;
TransactionId set_xid = 0;
uint32 minXlogId = 0,
minXlogSeg = 0;
int fd;
char path[MAXPGPATH];
for (argn = 1; argn < argc; argn++)
{
if (argv[argn][0] != '-')
break; /* end of switches */
if (strcmp(argv[argn], "-f") == 0)
force = true;
else if (strcmp(argv[argn], "-n") == 0)
noupdate = true;
else if (strcmp(argv[argn], "-x") == 0)
{
argn++;
if (argn == argc)
usage();
set_xid = strtoul(argv[argn], NULL, 0);
if (set_xid == 0)
{
fprintf(stderr, "XID can not be 0.\n");
exit(1);
}
}
else if (strcmp(argv[argn], "-l") == 0)
{
argn++;
if (argn == argc)
usage();
minXlogId = strtoul(argv[argn], NULL, 0);
argn++;
if (argn == argc)
usage();
minXlogSeg = strtoul(argv[argn], NULL, 0);
}
else
usage();
}
if (argn != argc - 1) /* one required non-switch argument */
usage();
DataDir = argv[argn++];
snprintf(XLogDir, MAXPGPATH, "%s/pg_xlog", DataDir);
snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
/*
* Check for a postmaster lock file --- if there is one, refuse to
* proceed, on grounds we might be interfering with a live
* installation.
*/
snprintf(path, MAXPGPATH, "%s/postmaster.pid", DataDir);
if ((fd = open(path, O_RDONLY)) < 0)
{
if (errno != ENOENT)
{
perror("Failed to open $PGDATA/postmaster.pid for reading");
exit(1);
}
}
else
{
fprintf(stderr, "Lock file '%s' exists --- is a postmaster running?\n"
"If not, delete the lock file and try again.\n",
path);
exit(1);
}
/*
* Attempt to read the existing pg_control file
*/
if (!ReadControlFile())
GuessControlValues();
/*
* If we had to guess anything, and -f was not given, just print the
* guessed values and exit. Also print if -n is given.
*/
if ((guessed && !force) || noupdate)
{
PrintControlValues(guessed);
if (!noupdate)
{
printf("\nIf these values seem acceptable, use -f to force reset.\n");
exit(1);
}
else
exit(0);
}
/*
* Don't reset from a dirty pg_control without -f, either.
*/
if (ControlFile.state != DB_SHUTDOWNED && !force)
{
printf("The database was not shut down cleanly.\n"
"Resetting the xlog may cause data to be lost!\n"
"If you want to proceed anyway, use -f to force reset.\n");
exit(1);
}
/*
* Else, do the dirty deed.
*
* First adjust fields if required by switches.
*/
if (set_xid != 0)
ControlFile.checkPointCopy.nextXid = set_xid;
if (minXlogId > ControlFile.logId ||
(minXlogId == ControlFile.logId && minXlogSeg > ControlFile.logSeg))
{
ControlFile.logId = minXlogId;
ControlFile.logSeg = minXlogSeg;
}
RewriteControlFile();
KillExistingXLOG();
WriteEmptyXLOG();
printf("XLOG reset.\n");
return 0;
}

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/pg_controldata.sgml,v 1.2 2002/08/16 20:36:25 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/pg_controldata.sgml,v 1.3 2002/08/17 02:43:08 momjian Exp $
PostgreSQL documentation
-->
@ -33,7 +33,8 @@ PostgreSQL documentation
</para>
<para>
This utility may only be run by the user who installed the server.
This utility may only be run by the user who installed the server because
it requires read access to the <literal>datadir</>.
You can specify the data directory on the command line, or use
the environment variable <envar>PGDATA</>.
</para>

View File

@ -4,7 +4,7 @@
#
# Copyright (c) 1998, PostgreSQL Global Development Group
#
# $Header: /cvsroot/pgsql/src/bin/pg_controldata/Makefile,v 1.1 2002/08/16 20:34:06 momjian Exp $
# $Header: /cvsroot/pgsql/src/bin/pg_controldata/Makefile,v 1.2 2002/08/17 02:43:08 momjian Exp $
#
#-------------------------------------------------------------------------
@ -17,7 +17,7 @@ OBJS= pg_controldata.o pg_crc.o
pg_crc.c: $(top_builddir)/src/backend/utils/hash/pg_crc.c
rm -f $@ && $(LN_S) $< .
all: submake-libpq submake-libpgport pg_controldata
all: submake-libpgport pg_controldata
pg_controldata: $(OBJS)
$(CC) $(CFLAGS) $^ $(libpq) $(LDFLAGS) $(LIBS) -o $@