From 6945ea34457822d487b73579cf0ff971c3ba0e88 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sat, 17 Aug 2002 02:43:08 +0000 Subject: [PATCH] Move pg_controldata to /bin. --- contrib/pg_resetxlog/Makefile | 19 - contrib/pg_resetxlog/README.pg_resetxlog | 42 -- contrib/pg_resetxlog/pg_resetxlog.c | 611 ----------------------- doc/src/sgml/ref/pg_controldata.sgml | 5 +- src/bin/pg_controldata/Makefile | 4 +- 5 files changed, 5 insertions(+), 676 deletions(-) delete mode 100644 contrib/pg_resetxlog/Makefile delete mode 100644 contrib/pg_resetxlog/README.pg_resetxlog delete mode 100644 contrib/pg_resetxlog/pg_resetxlog.c diff --git a/contrib/pg_resetxlog/Makefile b/contrib/pg_resetxlog/Makefile deleted file mode 100644 index 193e8bb93a..0000000000 --- a/contrib/pg_resetxlog/Makefile +++ /dev/null @@ -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 diff --git a/contrib/pg_resetxlog/README.pg_resetxlog b/contrib/pg_resetxlog/README.pg_resetxlog deleted file mode 100644 index 9802cdddd3..0000000000 --- a/contrib/pg_resetxlog/README.pg_resetxlog +++ /dev/null @@ -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. diff --git a/contrib/pg_resetxlog/pg_resetxlog.c b/contrib/pg_resetxlog/pg_resetxlog.c deleted file mode 100644 index 44925fc7c2..0000000000 --- a/contrib/pg_resetxlog/pg_resetxlog.c +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#ifdef USE_LOCALE -#include -#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; -} diff --git a/doc/src/sgml/ref/pg_controldata.sgml b/doc/src/sgml/ref/pg_controldata.sgml index 96e1bea55b..7b4be8c968 100644 --- a/doc/src/sgml/ref/pg_controldata.sgml +++ b/doc/src/sgml/ref/pg_controldata.sgml @@ -1,5 +1,5 @@ @@ -33,7 +33,8 @@ PostgreSQL documentation - 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 datadir. You can specify the data directory on the command line, or use the environment variable PGDATA. diff --git a/src/bin/pg_controldata/Makefile b/src/bin/pg_controldata/Makefile index d1e3d6537f..1afc0390b4 100644 --- a/src/bin/pg_controldata/Makefile +++ b/src/bin/pg_controldata/Makefile @@ -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 $@