📄 pg_resetxlog.c
字号:
/*------------------------------------------------------------------------- * * 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-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.38 2005/10/15 02:49:40 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <dirent.h>#include <fcntl.h>#include <locale.h>#include <sys/stat.h>#include <sys/time.h>#include <time.h>#include <unistd.h>#ifdef HAVE_GETOPT_H#include <getopt.h>#endif#include "access/multixact.h"#include "access/xlog.h"#include "access/xlog_internal.h"#include "catalog/catversion.h"#include "catalog/pg_control.h"extern int optind;extern char *optarg;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 */static const char *progname;static bool ReadControlFile(void);static void GuessControlValues(void);static void PrintControlValues(bool guessed);static void RewriteControlFile(void);static void KillExistingXLOG(void);static void WriteEmptyXLOG(void);static void usage(void);intmain(int argc, char *argv[]){ int c; bool force = false; bool noupdate = false; TransactionId set_xid = 0; Oid set_oid = 0; MultiXactId set_mxid = 0; MultiXactOffset set_mxoff = -1; uint32 minXlogTli = 0, minXlogId = 0, minXlogSeg = 0; char *endptr; char *endptr2; char *endptr3; char *DataDir; int fd; char path[MAXPGPATH]; set_pglocale_pgservice(argv[0], "pg_resetxlog"); progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_resetxlog (PostgreSQL) " PG_VERSION); exit(0); } } while ((c = getopt(argc, argv, "fl:m:no:O:x:")) != -1) { switch (c) { case 'f': force = true; break; case 'n': noupdate = true; break; case 'x': set_xid = strtoul(optarg, &endptr, 0); if (endptr == optarg || *endptr != '\0') { fprintf(stderr, _("%s: invalid argument for option -x\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (set_xid == 0) { fprintf(stderr, _("%s: transaction ID (-x) must not be 0\n"), progname); exit(1); } break; case 'o': set_oid = strtoul(optarg, &endptr, 0); if (endptr == optarg || *endptr != '\0') { fprintf(stderr, _("%s: invalid argument for option -o\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (set_oid == 0) { fprintf(stderr, _("%s: OID (-o) must not be 0\n"), progname); exit(1); } break; case 'm': set_mxid = strtoul(optarg, &endptr, 0); if (endptr == optarg || *endptr != '\0') { fprintf(stderr, _("%s: invalid argument for option -m\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (set_mxid == 0) { fprintf(stderr, _("%s: multitransaction ID (-m) must not be 0\n"), progname); exit(1); } break; case 'O': set_mxoff = strtoul(optarg, &endptr, 0); if (endptr == optarg || *endptr != '\0') { fprintf(stderr, _("%s: invalid argument for option -O\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } if (set_mxoff == -1) { fprintf(stderr, _("%s: multitransaction offset (-O) must not be -1\n"), progname); exit(1); } break; case 'l': minXlogTli = strtoul(optarg, &endptr, 0); if (endptr == optarg || *endptr != ',') { fprintf(stderr, _("%s: invalid argument for option -l\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } minXlogId = strtoul(endptr + 1, &endptr2, 0); if (endptr2 == endptr + 1 || *endptr2 != ',') { fprintf(stderr, _("%s: invalid argument for option -l\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } minXlogSeg = strtoul(endptr2 + 1, &endptr3, 0); if (endptr3 == endptr2 + 1 || *endptr3 != '\0') { fprintf(stderr, _("%s: invalid argument for option -l\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } } if (optind == argc) { fprintf(stderr, _("%s: no data directory specified\n"), progname); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } /* * Don't allow pg_resetxlog to be run as root, to avoid overwriting the * ownership of files in the data directory. We need only check for root * -- any other user won't have sufficient permissions to modify files in * the data directory. */#ifndef WIN32#ifndef __BEOS__ /* no root check on BeOS */ if (geteuid() == 0) { fprintf(stderr, _("%s: cannot be executed by \"root\"\n"), progname); fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"), progname); exit(1); }#endif#endif DataDir = argv[optind]; if (chdir(DataDir) < 0) { fprintf(stderr, _("%s: could not change directory to \"%s\": %s\n"), progname, DataDir, strerror(errno)); exit(1); } /* * 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) { fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, path, strerror(errno)); exit(1); } } else { fprintf(stderr, _("%s: lock file \"%s\" exists\n" "Is a server running? If not, delete the lock file and try again.\n"), progname, path); exit(1); } /* * Attempt to read the existing pg_control file */ if (!ReadControlFile()) GuessControlValues(); /* * Adjust fields if required by switches. (Do this now so that printout, * if any, includes these values.) */ if (set_xid != 0) ControlFile.checkPointCopy.nextXid = set_xid; if (set_oid != 0) ControlFile.checkPointCopy.nextOid = set_oid; if (set_mxid != 0) ControlFile.checkPointCopy.nextMulti = set_mxid; if (set_mxoff != -1) ControlFile.checkPointCopy.nextMultiOffset = set_mxoff; if (minXlogTli > ControlFile.checkPointCopy.ThisTimeLineID) ControlFile.checkPointCopy.ThisTimeLineID = minXlogTli; if (minXlogId > ControlFile.logId || (minXlogId == ControlFile.logId && minXlogSeg > ControlFile.logSeg)) { ControlFile.logId = minXlogId; ControlFile.logSeg = minXlogSeg; } /* * 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 server was not shut down cleanly.\n" "Resetting the transaction log 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. */ RewriteControlFile(); KillExistingXLOG(); WriteEmptyXLOG(); printf(_("Transaction log reset\n")); return 0;}/* * Try to read the existing pg_control file. * * This routine is also responsible for updating old pg_control versions * to the current format. (Currently we don't do anything of the sort.) */static boolReadControlFile(void){ int fd; int len; char *buffer; pg_crc32 crc; if ((fd = open(XLOG_CONTROL_FILE, 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. */ fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"), progname, XLOG_CONTROL_FILE, strerror(errno)); if (errno == ENOENT) fprintf(stderr, _("If you are sure the data directory path is correct, execute\n" " touch %s\n" "and try again.\n"), XLOG_CONTROL_FILE); exit(1); } /* Use malloc to ensure we have a maxaligned buffer */ buffer = (char *) malloc(BLCKSZ); len = read(fd, buffer, BLCKSZ); if (len < 0) { fprintf(stderr, _("%s: could not read file \"%s\": %s\n"), progname, XLOG_CONTROL_FILE, strerror(errno)); exit(1); } close(fd); if (len >= sizeof(ControlFileData) && ((ControlFileData *) buffer)->pg_control_version == PG_CONTROL_VERSION) { /* Check the CRC. */ INIT_CRC32(crc); COMP_CRC32(crc, buffer, offsetof(ControlFileData, crc)); FIN_CRC32(crc); if (EQ_CRC32(crc, ((ControlFileData *) buffer)->crc)) { /* Valid data... */ memcpy(&ControlFile, buffer, sizeof(ControlFile)); return true; } fprintf(stderr, _("%s: pg_control exists but has invalid CRC; proceed with caution\n"), progname); /* 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, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -