<commit_before>/*-------------------------------------------------------------------------
 *
 * 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 reset 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 rebuild the values,
 *		 by scanning the old xlog; if it fail then try to guess it.
 *	  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".
 *
 * The algorithm of restoring the pg_control value from old xlog file:
 *	1. Retrieve all of the active xlog files from xlog direcotry into a list 
 *	   by increasing order, according their timeline, log id, segment id.
 *	2. Search the list to find the oldest xlog file of the lastest time line.
 *	3. Search the records from the oldest xlog file of latest time line
 *	   to the latest xlog file of latest time line, if the checkpoint record
 *	   has been found, update the latest checkpoint and previous checkpoint.
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 *-------------------------------------------------------------------------
 */
#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"

#define GUESS	0
#define WAL	1

extern int	optind;
extern char *optarg;


static ControlFileData ControlFile;		/* pg_control values */
static uint32 newXlogId,
			newXlogSeg;			/* ID/Segment of new XLOG segment */
static const char *progname;
static uint64		sysidentifier=-1;

/* 
 * We use a list to store the active xlog files we had found in the 
 * xlog directory in increasing order according the time line, logid, 
 * segment id.
 * 
 */
typedef struct XLogFileName {
	TimeLineID tli; 
	uint32 logid; 
	uint32 seg;
	char fname[256];
	struct XLogFileName *next;
}	XLogFileName;

/* The list head */
static XLogFileName *xlogfilelist=NULL;

/* LastXLogfile is the latest file in the latest time line, 
   CurXLogfile is the oldest file in the lastest time line
   */
static XLogFileName *CurXLogFile, *LastXLogFile; 

/* The last checkpoint found in xlog file.*/
static CheckPoint      lastcheckpoint;

/* The last and previous checkpoint pointers found in xlog file.*/
static XLogRecPtr 	prevchkp, lastchkp; 

/* the database state.*/
static DBState	state=DB_SHUTDOWNED; 

/* the total checkpoint numbers which had been found in the xlog file.*/
static int 		found_checkpoint=0;	


static bool ReadControlFile(void);
static bool RestoreControlValues(int mode);
static void PrintControlValues(void);
static void UpdateCtlFile4Reset(void);
static void RewriteControlFile(void);
static void KillExistingXLOG(void);
static void WriteEmptyXLOG(void);
static void usage(void);

static void GetXLogFiles(void);
static bool ValidXLogFileName(char * fname);
static bool ValidXLogFileHeader(XLogFileName *segfile);
static bool ValidXLOGPageHeader(XLogPageHeader hdr, uint tli, uint id, uint seg);
static bool CmpXLogFileOT(XLogFileName * f1, XLogFileName *f2);
static bool IsNextSeg(XLogFileName *prev, XLogFileName *cur);
static void InsertXLogFile( char * fname );
static bool ReadXLogPage(void);
static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr);
static bool FetchRecord(void);
static void UpdateCheckPoint(XLogRecord *record);
static void SelectStartXLog(void);
static int SearchLastCheckpoint(void);
static int OpenXLogFile(XLogFileName *sf);
static void CleanUpList(XLogFileName *list);

int
main(int argc, char *argv[])
{
	int			c;
	bool		force = false;
	bool		restore = 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];
	bool		ctlcorrupted = false;
	bool		PidLocked = false;
	
	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:r")) != -1)
	{
		switch (c)
		{
			case 'f':
				force = true;
				break;
				
			case 'r':
				restore = 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
	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

	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
	{
		PidLocked = true;
	}

	/*
	 * Attempt to read the existing pg_control file
	 */
	if (!ReadControlFile())
	{
		/* The control file has been corruptted.*/
		ctlcorrupted = true;
	}

	/*
	 * 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;
	}

	/* retore the broken control file from WAL file.*/
	if (restore)
	{

		/* If the control fine is fine, don't touch it.*/
		if ( !ctlcorrupted )
		{
			printf(_("\nThe control file seems fine, not need to restore it.\n"));
			printf(_("If you want to restore it anyway, use -f option, but this also will reset the log file.\n"));
			exit(0);
		}
		
		
		/* Try to restore control values from old xlog file, or complain it.*/
		if (RestoreControlValues(WAL))
		{
			/* Success in restoring the checkpoint information from old xlog file.*/
			
			/* Print it out.*/
			PrintControlValues();

			/* In case the postmaster is crashed.
			 * But it may be dangerous for the living one.
			 * It may need a more good way.
			 */
			if (PidLocked)
			{
				ControlFile.state = DB_IN_PRODUCTION;
			}
			/* Write the new control file. */
			RewriteControlFile();
			printf(_("\nThe control file had been restored.\n"));
		} 
		else 
		{ 
			/* Fail in restoring the checkpoint information from old xlog file. */
			printf(_("\nCan not restore the control file from XLog file..\n"));
			printf(_("\nIf you want to restore it anyway, use -f option to guess the information, but this also will reset the log file.\n"));
		}

		exit(0);
		
	}	
	if (PidLocked)
	{  
		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);

	}
	/*
	* Print out the values in control file if -n is given. if the control file is 
	* corrupted, then inform user to restore it first.
	 */
	if (noupdate)
	{
		if (!ctlcorrupted)
		{
			/* The control file is fine, print the values out.*/
			PrintControlValues();
			exit(0);
		}
		else{
			/* The control file is corrupted.*/
			printf(_("The control file had been corrupted.\n"));
			printf(_("Please use -r option to restore it first.\n"));
			exit(1);
			}
	}

	/*
	 * Don't reset from a dirty pg_control without -f, either.
	 */
	if (ControlFile.state != DB_SHUTDOWNED && !force && !ctlcorrupted)
	{
		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);
	}

/*
	 * Try to reset the xlog file.
	 */
	 
	/* If the control file is corrupted, and -f option is given, resotre it first.*/
	if ( ctlcorrupted )
	{
		if (force)
		{
			if (!RestoreControlValues(WAL))
			{
				printf(_("fails to recover the control file from old xlog files, so we had to guess it.\n"));
				RestoreControlValues(GUESS);
			}
			printf(_("Restored the control file from old xlog files.\n"));
		}
		else
		{
			printf(_("Control file corrupted.\nIf you want to proceed anyway, use -f to force reset.\n"));
			exit(1);
			}
	} 
	
	/* Reset the xlog fille.*/
	UpdateCtlFile4Reset();
	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 bool
ReadControlFile(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(PG_CONTROL_SIZE);

	len = read(fd, buffer, PG_CONTROL_SIZE);
	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));
		return true;
	}

	/* Looks like it's a mess. */
	fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
			progname);
	return false;
}




/*
 *  Restore the pg_control values by scanning old xlog files or by guessing it.
 *
 * Input parameter:
 *	WAL:  Restore the pg_control values by scanning old xlog files.
 *	GUESS: Restore the pg_control values by guessing.
 * Return:
 *	TRUE: success in restoring.
 *	FALSE: fail to restore the values. 
 * 
 */
static bool 
RestoreControlValues(int mode)
{
	struct timeval tv;
	char	   *localeptr;
	bool	successed=true;

	/*
	 * Set up a completely default set of pg_control values.
	 */
	memset(&ControlFile, 0, sizeof(ControlFile));

	ControlFile.pg_control_version = PG_CONTROL_VERSION;
	ControlFile.catalog_version_no = CATALOG_VERSION_NO;

	/* 
	 * update the checkpoint value in control file,by searching 
	 * xlog segment file, or just guessing it.
	 */
	 if (mode == WAL)
	 {
		int result = SearchLastCheckpoint();
		if ( result > 0 ) /* The last checkpoint had been found. */
		{
			ControlFile.checkPointCopy = lastcheckpoint;
			ControlFile.checkPoint = lastchkp;
			ControlFile.prevCheckPoint = prevchkp;
			ControlFile.logId = LastXLogFile->logid;
			ControlFile.logSeg = LastXLogFile->seg + 1;
			ControlFile.checkPointCopy.ThisTimeLineID = LastXLogFile->tli;
			ControlFile.state = state;
		} else 	successed = false;
		
		/* Clean up the list. */
		CleanUpList(xlogfilelist);		
		
	 } 	
	
	if (mode == GUESS)
	{
		ControlFile.checkPointCopy.redo.xlogid = 0;
		ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
		ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
		ControlFile.checkPointCopy.nextXid = (TransactionId) 514;	/* XXX */
		ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
		ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
		ControlFile.checkPointCopy.nextMultiOffset = 0;
		ControlFile.checkPointCopy.time = time(NULL);
		ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
		/*
		 * Create a new unique installation identifier, since we can no longer
		 * use any old XLOG records.  See notes in xlog.c about the algorithm.
		 */
		gettimeofday(&tv, NULL);
		sysidentifier = ((uint64) tv.tv_sec) << 32;
		sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
		ControlFile.state = DB_SHUTDOWNED;
		
	}

	ControlFile.time = time(NULL);
	ControlFile.system_identifier = sysidentifier;
	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
	ControlFile.floatFormat = FLOATFORMAT_VALUE;
	ControlFile.blcksz = BLCKSZ;
	ControlFile.relseg_size = RELSEG_SIZE;
	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
	ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
	ControlFile.nameDataLen = NAMEDATALEN;
	ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
#ifdef HAVE_INT64_TIMESTAMP
	ControlFile.enableIntTimes = TRUE;
#else
	ControlFile.enableIntTimes = FALSE;
#endif
	ControlFile.localeBuflen = LOCALE_NAME_BUFLEN;

	localeptr = setlocale(LC_COLLATE, "");
	if (!localeptr)
	{
		fprintf(stderr, _("%s: invalid LC_COLLATE setting\n"), progname);
		exit(1);
	}
	StrNCpy(ControlFile.lc_collate, localeptr, LOCALE_NAME_BUFLEN);
	localeptr = setlocale(LC_CTYPE, "");
	if (!localeptr)
	{
		fprintf(stderr, _("%s: invalid LC_CTYPE setting\n"), progname);
		exit(1);
	}
	StrNCpy(ControlFile.lc_ctype, localeptr, LOCALE_NAME_BUFLEN);

	return successed;	
}


/*
 * Print the out pg_control values.
 *
 * NB: this display should be just those fields that will not be
 * reset by RewriteControlFile().
 */
static void
PrintControlValues(void)
{
	char		sysident_str[32];

	printf(_("pg_control values:\n\n"));

	/*
	 * Format system_identifier separately to keep platform-dependent format
	 * code out of the translatable message string.<commit_msg>Add missing ControlFile.checkPointCopy.ThisTimeLineID line for 'guess' pg_resetxlog.<commit_after>/*-------------------------------------------------------------------------
 *
 * 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 reset 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 rebuild the values,
 *		 by scanning the old xlog; if it fail then try to guess it.
 *	  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".
 *
 * The algorithm of restoring the pg_control value from old xlog file:
 *	1. Retrieve all of the active xlog files from xlog direcotry into a list 
 *	   by increasing order, according their timeline, log id, segment id.
 *	2. Search the list to find the oldest xlog file of the lastest time line.
 *	3. Search the records from the oldest xlog file of latest time line
 *	   to the latest xlog file of latest time line, if the checkpoint record
 *	   has been found, update the latest checkpoint and previous checkpoint.
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 *-------------------------------------------------------------------------
 */
#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"

#define GUESS	0
#define WAL	1

extern int	optind;
extern char *optarg;


static ControlFileData ControlFile;		/* pg_control values */
static uint32 newXlogId,
			newXlogSeg;			/* ID/Segment of new XLOG segment */
static const char *progname;
static uint64		sysidentifier=-1;

/* 
 * We use a list to store the active xlog files we had found in the 
 * xlog directory in increasing order according the time line, logid, 
 * segment id.
 * 
 */
typedef struct XLogFileName {
	TimeLineID tli; 
	uint32 logid; 
	uint32 seg;
	char fname[256];
	struct XLogFileName *next;
}	XLogFileName;

/* The list head */
static XLogFileName *xlogfilelist=NULL;

/* LastXLogfile is the latest file in the latest time line, 
   CurXLogfile is the oldest file in the lastest time line
   */
static XLogFileName *CurXLogFile, *LastXLogFile; 

/* The last checkpoint found in xlog file.*/
static CheckPoint      lastcheckpoint;

/* The last and previous checkpoint pointers found in xlog file.*/
static XLogRecPtr 	prevchkp, lastchkp; 

/* the database state.*/
static DBState	state=DB_SHUTDOWNED; 

/* the total checkpoint numbers which had been found in the xlog file.*/
static int 		found_checkpoint=0;	


static bool ReadControlFile(void);
static bool RestoreControlValues(int mode);
static void PrintControlValues(void);
static void UpdateCtlFile4Reset(void);
static void RewriteControlFile(void);
static void KillExistingXLOG(void);
static void WriteEmptyXLOG(void);
static void usage(void);

static void GetXLogFiles(void);
static bool ValidXLogFileName(char * fname);
static bool ValidXLogFileHeader(XLogFileName *segfile);
static bool ValidXLOGPageHeader(XLogPageHeader hdr, uint tli, uint id, uint seg);
static bool CmpXLogFileOT(XLogFileName * f1, XLogFileName *f2);
static bool IsNextSeg(XLogFileName *prev, XLogFileName *cur);
static void InsertXLogFile( char * fname );
static bool ReadXLogPage(void);
static bool RecordIsValid(XLogRecord *record, XLogRecPtr recptr);
static bool FetchRecord(void);
static void UpdateCheckPoint(XLogRecord *record);
static void SelectStartXLog(void);
static int SearchLastCheckpoint(void);
static int OpenXLogFile(XLogFileName *sf);
static void CleanUpList(XLogFileName *list);

int
main(int argc, char *argv[])
{
	int			c;
	bool		force = false;
	bool		restore = 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];
	bool		ctlcorrupted = false;
	bool		PidLocked = false;
	
	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:r")) != -1)
	{
		switch (c)
		{
			case 'f':
				force = true;
				break;
				
			case 'r':
				restore = 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
	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

	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
	{
		PidLocked = true;
	}

	/*
	 * Attempt to read the existing pg_control file
	 */
	if (!ReadControlFile())
	{
		/* The control file has been corruptted.*/
		ctlcorrupted = true;
	}

	/*
	 * 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;
	}

	/* retore the broken control file from WAL file.*/
	if (restore)
	{

		/* If the control fine is fine, don't touch it.*/
		if ( !ctlcorrupted )
		{
			printf(_("\nThe control file seems fine, not need to restore it.\n"));
			printf(_("If you want to restore it anyway, use -f option, but this also will reset the log file.\n"));
			exit(0);
		}
		
		
		/* Try to restore control values from old xlog file, or complain it.*/
		if (RestoreControlValues(WAL))
		{
			/* Success in restoring the checkpoint information from old xlog file.*/
			
			/* Print it out.*/
			PrintControlValues();

			/* In case the postmaster is crashed.
			 * But it may be dangerous for the living one.
			 * It may need a more good way.
			 */
			if (PidLocked)
			{
				ControlFile.state = DB_IN_PRODUCTION;
			}
			/* Write the new control file. */
			RewriteControlFile();
			printf(_("\nThe control file had been restored.\n"));
		} 
		else 
		{ 
			/* Fail in restoring the checkpoint information from old xlog file. */
			printf(_("\nCan not restore the control file from XLog file..\n"));
			printf(_("\nIf you want to restore it anyway, use -f option to guess the information, but this also will reset the log file.\n"));
		}

		exit(0);
		
	}	
	if (PidLocked)
	{  
		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);

	}
	/*
	* Print out the values in control file if -n is given. if the control file is 
	* corrupted, then inform user to restore it first.
	 */
	if (noupdate)
	{
		if (!ctlcorrupted)
		{
			/* The control file is fine, print the values out.*/
			PrintControlValues();
			exit(0);
		}
		else{
			/* The control file is corrupted.*/
			printf(_("The control file had been corrupted.\n"));
			printf(_("Please use -r option to restore it first.\n"));
			exit(1);
			}
	}

	/*
	 * Don't reset from a dirty pg_control without -f, either.
	 */
	if (ControlFile.state != DB_SHUTDOWNED && !force && !ctlcorrupted)
	{
		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);
	}

/*
	 * Try to reset the xlog file.
	 */
	 
	/* If the control file is corrupted, and -f option is given, resotre it first.*/
	if ( ctlcorrupted )
	{
		if (force)
		{
			if (!RestoreControlValues(WAL))
			{
				printf(_("fails to recover the control file from old xlog files, so we had to guess it.\n"));
				RestoreControlValues(GUESS);
			}
			printf(_("Restored the control file from old xlog files.\n"));
		}
		else
		{
			printf(_("Control file corrupted.\nIf you want to proceed anyway, use -f to force reset.\n"));
			exit(1);
			}
	} 
	
	/* Reset the xlog fille.*/
	UpdateCtlFile4Reset();
	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 bool
ReadControlFile(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(PG_CONTROL_SIZE);

	len = read(fd, buffer, PG_CONTROL_SIZE);
	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));
		return true;
	}

	/* Looks like it's a mess. */
	fprintf(stderr, _("%s: pg_control exists but is broken or unknown version; ignoring it\n"),
			progname);
	return false;
}




/*
 *  Restore the pg_control values by scanning old xlog files or by guessing it.
 *
 * Input parameter:
 *	WAL:  Restore the pg_control values by scanning old xlog files.
 *	GUESS: Restore the pg_control values by guessing.
 * Return:
 *	TRUE: success in restoring.
 *	FALSE: fail to restore the values. 
 * 
 */
static bool 
RestoreControlValues(int mode)
{
	struct timeval tv;
	char	   *localeptr;
	bool	successed = true;

	/*
	 * Set up a completely default set of pg_control values.
	 */
	memset(&ControlFile, 0, sizeof(ControlFile));

	ControlFile.pg_control_version = PG_CONTROL_VERSION;
	ControlFile.catalog_version_no = CATALOG_VERSION_NO;

	/* 
	 * update the checkpoint value in control file,by searching 
	 * xlog segment file, or just guessing it.
	 */
	if (mode == WAL)
	{
		int result = SearchLastCheckpoint();

		if (result > 0) /* The last checkpoint had been found. */
		{
			ControlFile.checkPointCopy = lastcheckpoint;
			ControlFile.checkPointCopy.ThisTimeLineID = LastXLogFile->tli;
			ControlFile.checkPoint = lastchkp;
			ControlFile.prevCheckPoint = prevchkp;

			ControlFile.logId = LastXLogFile->logid;
			ControlFile.logSeg = LastXLogFile->seg + 1;
			ControlFile.state = state;
		}
		else
			successed = false;
		
		/* Clean up the list. */
		CleanUpList(xlogfilelist);		
	}
	else	/* GUESS */
	{
		ControlFile.checkPointCopy.ThisTimeLineID = 2;
		ControlFile.checkPointCopy.redo.xlogid = 0;
		ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
		ControlFile.checkPointCopy.undo = ControlFile.checkPointCopy.redo;
		ControlFile.checkPointCopy.nextXid = (TransactionId) 514;	/* XXX */
		ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
		ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
		ControlFile.checkPointCopy.nextMultiOffset = 0;
		ControlFile.checkPointCopy.time = time(NULL);
		ControlFile.checkPoint = ControlFile.checkPointCopy.redo;

		/*
		 * Create a new unique installation identifier, since we can no longer
		 * use any old XLOG records.  See notes in xlog.c about the algorithm.
		 */
		gettimeofday(&tv, NULL);
		sysidentifier = ((uint64) tv.tv_sec) << 32;
		sysidentifier |= (uint32) (tv.tv_sec | tv.tv_usec);
		ControlFile.state = DB_SHUTDOWNED;
		
	}

	ControlFile.time = time(NULL);
	ControlFile.system_identifier = sysidentifier;
	ControlFile.maxAlign = MAXIMUM_ALIGNOF;
	ControlFile.floatFormat = FLOATFORMAT_VALUE;
	ControlFile.blcksz = BLCKSZ;
	ControlFile.relseg_size = RELSEG_SIZE;
	ControlFile.xlog_blcksz = XLOG_BLCKSZ;
	ControlFile.xlog_seg_size = XLOG_SEG_SIZE;
	ControlFile.nameDataLen = NAMEDATALEN;
	ControlFile.indexMaxKeys = INDEX_MAX_KEYS;
#ifdef HAVE_INT64_TIMESTAMP
	ControlFile.enableIntTimes = TRUE;
#else
	ControlFile.enableIntTimes = FALSE;
#endif
	ControlFile.localeBuflen = LOCALE_NAME_BUFLEN;

	localeptr = setlocale(LC_COLLATE, "");
	if (!localeptr)
	{
		fprintf(stderr, _("%s: invalid LC_COLLATE setting\n"), progname);
		exit(1);
	}
	StrNCpy(ControlFile.lc_collate, localeptr, LOCALE_NAME_BUFLEN);

	localeptr = setlocale(LC_CTYPE, "");
	if (!localeptr)
	{
		fprintf(stderr, _("%s: invalid LC_CTYPE setting\n"), progname);
		exit(1);
	}
	StrNCpy(ControlFile.lc_ctype, localeptr, LOCALE_NAME_BUFLEN);

	return successed;	
}


/*
 * Print the out pg_control values.
 *
 * NB: this display should be just those fields that will not be
 * reset by RewriteControlFile().
 */
static void
PrintControlValues(void)
{
	char		sysident_str[32];

	printf(_("pg_control values:\n\n"));

	/*
	 * Format system_identifier separately to keep platform-dependent format
	 * code out of the translatable message string.
{'fraction_non_alphanumeric': 0.1325452985954755, 'fraction_numerical': 0.008711268360673314, 'mean_word_length': 3.281992425111902, 'pattern_counts': {'":': 4, '<': 31, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '803205', 'n_tokens_mistral': 14390, 'n_tokens_neox': 12617, 'n_words': 3953}