Re: Review: Patch to compute Max LSN of Data Pages

Started by Amit kapilaalmost 13 years ago32 messages
#1Amit kapila
amit.kapila@huawei.com
1 attachment(s)

Please find the rebased Patch for Compute MAX LSN.

There was one compilation error as "undefined reference to XLByteLT " as earlier XLogRecPtr was a structure as
typedef struct XLogRecPtr
{
uint32 xlogid; /* log file #, 0 based */
uint32 xrecoff; /* byte offset of location in log file */
} XLogRecPtr;
So in order to compare two LSN, there was one macro as XLByteLT to compare both fields.
But now XLogRecPtr has been changed as just uint64 and so XLByteLT is removed.
So the change done is to replace XLByteLT(*maxlsn, pagelsn) with (*maxlsn < pagelsn).

Muhammad, Can you verify if every thing is okay, then this can be marked as "Ready for Committer"

With Regards,

Amit Kapila.

Attachments:

pg_computemaxlsn_v5.patchapplication/octet-stream; name=pg_computemaxlsn_v5.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,828 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ #define validateTablespaceDir(name) ((strlen(name) > 3) && (name[0] == 'P') && (name[1] == 'G') && (name[2] == '_'))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ 
+ static int	FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static int	FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn, bool is_fromlink);
+ static int	FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ static int	getLinkPath(struct stat * statbuf, char *path, char *linkpath, int length);
+ 
+ /*
+  * Removes the parent references
+  * The input *must* have been put through canonicalize_path previously.
+  */
+ static void
+ remove_parent_refernces(char *inpath)
+ {
+ 	char	   *path;
+ 	char	   *epath;
+ 	char	   *edestpath;
+ 	int			len;
+ 	int			i;
+ 	int			pending_strips = 0;
+ 
+ 	len = strlen(inpath);
+ 	path = malloc(len + 2);
+ 	if (!path)
+ 	{
+ 		return;
+ 	}
+ 	MemSet(path, 0, len + 2);
+ 
+ 	epath = inpath + len;
+ 	edestpath = path + len;
+ 
+ 	for (i = len; i >= 0; i--, epath--)
+ 	{
+ 		if (IS_DIR_SEP(*epath))
+ 		{
+ 			if ((i > 3) && (*(epath - 1) == '.')
+ 				&& (*(epath - 2) == '.') && IS_DIR_SEP(*(epath - 3)))
+ 			{
+ 				/* Matching "/../" - trim the parent directoy */
+ 				if (!pending_strips)
+ 					pending_strips = 1;
+ 				pending_strips++;
+ 				i -= 2;
+ 				epath -= 2;
+ 			}
+ 			else if ((i > 2) && (*(epath - 1) == '.') && IS_DIR_SEP(*(epath - 2)))
+ 			{
+ 				/* Matching "/./" - can be skiped */
+ 				i -= 1;
+ 				epath -= 1;
+ 				continue;
+ 			}
+ 			else if (pending_strips)
+ 			{
+ 				pending_strips--;
+ 			}
+ 		}
+ 
+ 		if (pending_strips == 0)
+ 		{
+ 			*edestpath = *epath;
+ 			edestpath--;
+ 		}
+ 	}
+ 
+ 	if (pending_strips)
+ 	{
+ 		/* "/../", "/../../" are considered as "/" */
+ 		if (is_absolute_path(inpath))
+ 		{
+ 			*(inpath++) = '/';
+ 		}
+ 		else
+ 		{
+ 			/* Incase of relative path prefix with "../" */
+ 			while (pending_strips--)
+ 			{
+ 				strcpy(inpath, "../");
+ 				inpath += 3;
+ 			}
+ 		}
+ 	}
+ 
+ 	strcpy(inpath, edestpath + 1);
+ 	free(path);
+ 
+ 	return;
+ }
+ 
+ /*
+  * If the given pathname isn't already absolute, make it so, interpreting
+  * it relative to the current working directory.
+  */
+ static char *
+ make_absolute_path(const char *in)
+ {
+ 	char	   *result;
+ 
+ 	if (is_absolute_path(in))
+ 		result = strdup(in);
+ 	else
+ 	{
+ 		char		cwdbuf[MAXPGPATH];
+ 
+ 		if (!getcwd(cwdbuf, sizeof(cwdbuf)))
+ 		{
+ 			fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
+ 			exit(2);
+ 		}
+ 
+ 		result = malloc(strlen(cwdbuf) + strlen(in) + 2);
+ 		sprintf(result, "%s/%s", cwdbuf, in);
+ 	}
+ 
+ 	canonicalize_path(result);
+ 	remove_parent_refernces(result);
+ 	return result;
+ }
+ 
+ /*
+  *	This function validates the given cluster directory - we search for a
+  *	small set of subdirectories that we expect to find in a valid data directory.
+  *	directory.	If any of the subdirectories are missing (or secured against
+  *	us) we display an error message and exit()
+  *
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0)
+ 		{
+ 			fprintf(stderr, _("%s: check for \"%s\" failed: %s\n"),
+ 					progname, subDirName, strerror(errno));
+ 			return false;
+ 		}
+ 		else if (!S_ISDIR(statBuf.st_mode))
+ 		{
+ 			fprintf(stderr, _("%s: %s is not a directory.\n"), progname, subDirName);
+ 			return false;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * This function validates the given path is in or below cluster directory or
+  * any one of "base", "global", or "pg_tblspc" subdirectory.
+  */
+ static bool
+ check_path_belongs_to_pgdata(const char *in_pg_data, char *in_path,
+ 							 bool *is_inside_pg_data, bool *path_are_same)
+ {
+ 	char	   *usrpath;
+ 	char	   *pg_data;
+ 	int			data_path_len;
+ 
+ 	pg_data = make_absolute_path(in_pg_data);
+ 	usrpath = make_absolute_path(in_path);
+ 
+ 	data_path_len = strlen(pg_data);
+ 
+ 	/* check input usrpath is subdirectory of pg_data usrpath */
+ 	if (strncmp(pg_data, usrpath, data_path_len) == 0)
+ 	{
+ 		if (usrpath[data_path_len] == '\0')
+ 		{
+ 			*path_are_same = true;
+ 			*is_inside_pg_data = true;
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 		else if (!IS_DIR_SEP(usrpath[data_path_len]))
+ 		{
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return false;
+ 		}
+ 
+ 		*is_inside_pg_data = true;
+ 
+ 		/* Input usrpath same as pg_data but ending with ending with DIR_SEP */
+ 		if (usrpath[data_path_len + 1] == '\0')
+ 		{
+ 			*path_are_same = true;
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 
+ 		/*
+ 		 * Input usrpath is subdirectory of pg_data usrpath, so validate the
+ 		 * remaining usrpath it should be inside any one of "base", "global",
+ 		 * "pg_tablespc"
+ 		 */
+ 		if ((path_is_prefix_of_path("base", usrpath + (data_path_len + 1)))
+ 			|| (path_is_prefix_of_path("global", usrpath + (data_path_len + 1)))
+ 			|| (path_is_prefix_of_path("pg_tblspc", usrpath + (data_path_len + 1))))
+ 		{
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 	}
+ 	free(pg_data);
+ 	free(usrpath);
+ 
+ 	return false;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name, bool with_ext)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (with_ext)
+ 	{
+ 		if (name[pos] == '_')
+ 		{
+ 			pos++;
+ 			while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 				pos++;
+ 		}
+ 		if (name[pos] == '.')
+ 		{
+ 			pos++;
+ 			while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 				pos++;
+ 		}
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"path", required_argument, NULL, 'p'},
+ 		{"data-directory", no_argument, NULL, 'P'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 
+ 	int			optindex;
+ 	int			c;
+ 	char	   *DataDir;
+ 	int			fd;
+ 	char		path[MAXPGPATH];
+ 	bool		print_max_lsn = false;
+ 	bool		print_pgdata_max_lsn = false;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	int			result = 0;
+ 	bool		is_whole_data_dir = false;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "p:P", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'p':
+ 				if (print_max_lsn)
+ 				{
+ 					fprintf(stderr, _("%s: multiple -p options are not supported.\n"), progname);
+ 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 					exit(1);
+ 				}
+ 				print_max_lsn = true;
+ 				LsnSearchPath = strdup(optarg);
+ 				break;
+ 
+ 			case 'P':
+ 				if (print_pgdata_max_lsn)
+ 				{
+ 					fprintf(stderr, _("%s: multiple -P options are not supported.\n"), progname);
+ 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 					exit(1);
+ 				}
+ 				print_pgdata_max_lsn = true;
+ 				break;
+ 
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	if (print_max_lsn && print_pgdata_max_lsn)
+ 	{
+ 		fprintf(stderr, _("%s: both options -P and -p can not be combined.\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);
+ 	}
+ 
+ 	if ((optind + 1) != argc)
+ 	{
+ 		fprintf(stderr, _("%s: multiple data directories not supported.\n"), progname);
+ 		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 		exit(1);
+ 	}
+ 
+ 	/*
+ 	 * Don't allow pg_computemaxlsn 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 = strdup(argv[optind]);
+ 
+ 	canonicalize_path(DataDir);
+ 
+ 
+ 	if (!check_data_dir(DataDir))
+ 	{
+ 		fprintf(stderr, _("%s: \"%s\" not a valid  data directory.\n"),
+ 				progname, DataDir);
+ 		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)) < 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);
+ 	}
+ 
+ 	if (print_max_lsn)
+ 	{
+ 		struct stat		fst;
+ 		bool			is_inside_pg_data = false;
+ 
+ 		canonicalize_path(LsnSearchPath);
+ 
+ 		if (!check_path_belongs_to_pgdata(DataDir, LsnSearchPath, &is_inside_pg_data, &is_whole_data_dir))
+ 		{
+ 			if (is_inside_pg_data)
+ 			{
+ 				fprintf(stderr, _("%s: Path \"%s\" should be in or below any of \"base\",\"global\" or \"pg_tblspc\" of data directory.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			else
+ 			{
+ 				fprintf(stderr, _("%s: Path \"%s\" should be in or below data directory.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			exit(1);
+ 		}
+ 
+ 		if (is_whole_data_dir)
+ 		{
+ 			/* Process for whole data directory. */
+ 		}
+ 		else if (lstat(LsnSearchPath, &fst) < 0)
+ 		{
+ 			if (errno == ENOENT)
+ 			{
+ 				fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			else
+ 			{
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, LsnSearchPath, strerror(errno));
+ 			}
+ 			exit(1);
+ 		}
+ 		else if (getLinkPath(&fst, LsnSearchPath, path, sizeof(path)) > 0)
+ 		{
+ 			result = FindMaxLSNinDir(path, &maxLSN, true);
+ 		}
+ 		else if (S_ISDIR(fst.st_mode))
+ 		{
+ 			result = FindMaxLSNinDir(LsnSearchPath, &maxLSN, false);
+ 		}
+ 		else if (S_ISREG(fst.st_mode))
+ 		{
+ 			result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 		}
+ 		else
+ 		{
+ 			fprintf(stderr, _("%s: skipping special file \"%s\"\n"), progname, LsnSearchPath);
+ 		}
+ 	}
+ 
+ 	/* By default we need to compute max lsn for database */
+ 	if ((print_max_lsn == false) || is_whole_data_dir)
+ 	{
+ 		result = FindMaxLSNinPgData(DataDir, &maxLSN);
+ 	}
+ 
+ 	if (0 != result)
+ 	{
+ 		/* Message already provided, simply exit */
+ 		exit(1);
+ 	}
+ 
+ 	XLByteToSeg(maxLSN, logSegNo);
+ 
+ 	printf("Maximum LSN found is: %X/%X \nWAL segment file name (fileid,seg): %X/%X\n",
+ 		   (uint32)(maxLSN >> 32), (uint32) maxLSN,
+ 		   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * PageHeaderIsValid: Check page is valid or not
+  */
+ bool
+ PageHeaderIsValid(PageHeader page)
+ {
+ 	char	   *pagebytes;
+ 	int			i;
+ 
+ 	/* Check normal case */
+ 	if (PageGetPageSize(page) == BLCKSZ &&
+ 		PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
+ 		(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
+ 		page->pd_lower >= SizeOfPageHeaderData &&
+ 		page->pd_lower <= page->pd_upper &&
+ 		page->pd_upper <= page->pd_special &&
+ 		page->pd_special <= BLCKSZ &&
+ 		page->pd_special == MAXALIGN(page->pd_special))
+ 		return true;
+ 
+ 	/*
+ 	 * Check all-zeroes till page header; this is used only to log the page
+ 	 * details even we detect invalid page we will continue to next pages
+ 	 */
+ 	pagebytes = (char *) page;
+ 	for (i = 0; i < PAGEHDRSZ; i++)
+ 	{
+ 		if (pagebytes[i] != 0)
+ 			return false;
+ 	}
+ 	return true;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  *
+  */
+ static int
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return -1;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and validate; if we find invalid page log the
+ 	 * details of page and continue to next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to next page  \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		if (PageHeaderIsValid((PageHeader) buffer))
+ 		{
+ 			pagelsn = PageGetLSN(buffer);
+ 			if (*maxlsn < pagelsn)
+ 			{
+ 				*maxlsn = pagelsn;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/*
+ 			 * If page is invalid log the error and continue
+ 			 */
+ 			fprintf(stderr, _("%s: Invalid page found in file \"%s\" pagid:%d\n"),
+ 					progname, filename, blocknum);
+ 		}
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 	return 0;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static int
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn, bool is_fromlink)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 	char		linkpath[MAXPGPATH];
+ 	int			result;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all the local/global temporary files, and read and read all
+ 		 * reamining relfinenode files
+ 		 */
+ 		if (is_fromlink)
+ 		{
+ 			/* If directory is link then only allow PG_* path only */
+ 			if (!validateTablespaceDir(de->d_name))
+ 				continue;
+ 		}
+ 		else if (!validateRelfilenodename(de->d_name, true))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 			{
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 			}
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		result = getLinkPath(&statbuf, pathbuf, linkpath, sizeof(linkpath));
+ 		if (result < 0)
+ 		{
+ 			continue;
+ 		}
+ 		else if (result > 0)
+ 		{
+ 			(void) FindMaxLSNinDir(linkpath, maxlsn, true);
+ 		}
+ 		else if (S_ISDIR(statbuf.st_mode))
+ 			(void) FindMaxLSNinDir(pathbuf, maxlsn, false);
+ 		else
+ 			(void) FindMaxLSNinFile(pathbuf, maxlsn);
+ 	}
+ 
+ 	closedir(dir);
+ 	return 0;
+ }
+ 
+ /*
+  * Get the link path.
+  * On success, returns length of link filename.
+  * and return zero incase of is file is not a link type.
+  * On failure, returns -1.
+  */
+ static int
+ getLinkPath(struct stat * statbuf, char *path, char *linkpath, int length)
+ {
+ 	int			rllen;
+ 
+ 	if (
+ #ifndef WIN32
+ 		S_ISLNK(statbuf->st_mode)
+ #else
+ 		pgwin32_is_junction(path)
+ #endif
+ 		)
+ 	{
+ #if defined(HAVE_READLINK) || defined(WIN32)
+ 
+ 		rllen = readlink(path, linkpath, length);
+ 		if (rllen < 0)
+ 		{
+ 			fprintf(stderr, _("%s: could not read symbolic link \"%s\", so skipping file.\n"),
+ 					progname, path);
+ 			return -1;
+ 		}
+ 
+ 		if (rllen >= length)
+ 		{
+ 			fprintf(stderr, _("%s: symbolic link \"%s\" target is too long, so skipping file.\n"),
+ 					progname, path);
+ 
+ 			return -1;
+ 		}
+ 
+ 		linkpath[rllen] = '\0';
+ 
+ 		return rllen;
+ #else
+ 		/* tablespaces are not supported on this platform */
+ 		return -1;
+ #endif   /* HAVE_READLINK */
+ 	}
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static int
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	return 0;
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -p, --path=FILE_FOLDER_PATH   print max LSN from file or directory name\n"));
+ 	printf(_("  -P, --data-directory          print max LSN from whole data directory\n"));
+ 	printf(_("  -V, --version                 output version information, then exit\n"));
+ 	printf(_("  -?, --help                    show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 177,182 **** Complete list of usable sgml source files in this directory.
--- 177,183 ----
  <!ENTITY pgDumpall          SYSTEM "pg_dumpall.sgml">
  <!ENTITY pgReceivexlog      SYSTEM "pg_receivexlog.sgml">
  <!ENTITY pgResetxlog        SYSTEM "pg_resetxlog.sgml">
+ <!ENTITY pgComputemaxlsn    SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgRestore          SYSTEM "pg_restore.sgml">
  <!ENTITY postgres           SYSTEM "postgres-ref.sgml">
  <!ENTITY postmaster         SYSTEM "postmaster.sgml">
*** /dev/null
--- b/doc/src/sgml/ref/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,79 ----
+ <!--
+ doc/src/sgml/ref/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="APP-PGCOMPUTEMAXLSN">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="app-pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><option>-P</option></arg>
+    <arg choice="opt"><option>-p</option> <replaceable class="parameter">file-name</replaceable> | <replaceable class="parameter">folder-name</replaceable></arg>
+    <arg choice="plain"><replaceable>datadir</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages.
+   </para>
+ 
+   <para>
+    This utility can only be run by the user who installed the server, because
+    it requires read/write access to the data directory.
+    For safety reasons, you must specify the data directory on the command line.
+    <command>pg_computemaxlsn</command> does not use the environment variable
+    <envar>PGDATA</>.
+   </para>
+ 
+   <para>
+    The <option>-P</> or <option>--data-directory</> for computing maximum LSN from all the pages in data directory.
+    This is the default option if none of the options are provided.
+   </para>
+ 
+   <para>
+    The <option>-p <replaceable class="parameter">file-name | folder-name</replaceable></option> or
+    <option>--path=<replaceable class="parameter">file-name | folder-name</replaceable></option> for computing
+    maximun LSN from specific file or folder. File or folder path should be in or below the data directory.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options print
+    the <application>pg_computemaxlsn</application> version and exit.  The
+    options <option>-?</> and <option>--help</> show supported arguments,
+    and exit.
+   </para>
+ 
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+ 
+   <para>
+    This command must not be used when the server is
+    running.  <command>pg_computemaxlsn</command> will refuse to start up if
+    it finds a server lock file in the data directory.  If the
+    server crashed then a lock file might have been left
+    behind; in that case you can remove the lock file to allow
+    <command>pg_computemaxlsn</command> to run.  But before you do
+    so, make doubly certain that there is no server process still alive.
+   </para>
+  </refsect1>
+ 
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 135,140 **** PostgreSQL documentation
--- 135,150 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       with <option>-P</> option for finding maximum LSN from the data directory or 
+       for from specific file or folder <option>-p <filename>file-name | folder-name</></>.
+       Once larger WAL segment file is found use <option>-l</> option for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 248,253 ****
--- 248,254 ----
     &pgControldata;
     &pgCtl;
     &pgResetxlog;
+    &pgComputemaxlsn;
     &postgres;
     &postmaster;
  
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 34,40 **** my @contrib_uselibpgport = (
  	'oid2name',      'pgbench',
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
! 	'pg_upgrade',    'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 34,40 ----
  	'oid2name',      'pgbench',
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
! 	'pg_upgrade',    'vacuumlo', 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#2Dickson S. Guedes
listas@guedesoft.net
In reply to: Amit kapila (#1)

2013/1/18 Amit kapila <amit.kapila@huawei.com>:

Please find the rebased Patch for Compute MAX LSN.

The function 'remove_parent_refernces' couldn't be called
'remove_parent_references' ?

Why not an extension in PGXN instead of a contrib?

Regards,
--
Dickson S. Guedes
mail/xmpp: guedes@guedesoft.net - skype: guediz
http://github.com/guedes - http://guedesoft.net
http://www.postgresql.org.br

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Amit kapila
amit.kapila@huawei.com
In reply to: Dickson S. Guedes (#2)

On Sunday, January 20, 2013 4:04 AM Dickson S. Guedes wrote:
2013/1/18 Amit kapila <amit.kapila@huawei.com>:

Please find the rebased Patch for Compute MAX LSN.

The function 'remove_parent_refernces' couldn't be called
'remove_parent_references' ?

Shall fix this.

Why not an extension in PGXN instead of a contrib?

This functionality has similarity to pg_resetxlog, so we thought of putting it either in bin or in contrib.
Finally based on suggestions from other community members, we have added to contrib.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Dickson S. Guedes
listas@guedesoft.net
In reply to: Amit kapila (#3)

2013/1/20 Amit kapila <amit.kapila@huawei.com>:

On Sunday, January 20, 2013 4:04 AM Dickson S. Guedes wrote:
2013/1/18 Amit kapila <amit.kapila@huawei.com>:

Please find the rebased Patch for Compute MAX LSN.

The function 'remove_parent_refernces' couldn't be called
'remove_parent_references' ?

Shall fix this.

Why not an extension in PGXN instead of a contrib?

This functionality has similarity to pg_resetxlog, so we thought of putting it either in bin or in contrib.
Finally based on suggestions from other community members, we have added to contrib.

Indeed.

--
Dickson S. Guedes
mail/xmpp: guedes@guedesoft.net - skype: guediz
http://github.com/guedes - http://guedesoft.net
http://www.postgresql.org.br

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Amit kapila
amit.kapila@huawei.com
In reply to: Amit kapila (#3)
1 attachment(s)

On Sunday, January 20, 2013 10:50 AM Amit kapila wrote:
On Sunday, January 20, 2013 4:04 AM Dickson S. Guedes wrote:
2013/1/18 Amit kapila <amit.kapila@huawei.com>:

Please find the rebased Patch for Compute MAX LSN.

The function 'remove_parent_refernces' couldn't be called
'remove_parent_references' ?

Shall fix this.

Fixed in attached version.

With Regards,
Amit Kapila.

Attachments:

pg_computemaxlsn_v6.patchapplication/octet-stream; name=pg_computemaxlsn_v6.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,828 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ #define validateTablespaceDir(name) ((strlen(name) > 3) && (name[0] == 'P') && (name[1] == 'G') && (name[2] == '_'))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ 
+ static int	FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static int	FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn, bool is_fromlink);
+ static int	FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ static int	getLinkPath(struct stat * statbuf, char *path, char *linkpath, int length);
+ 
+ /*
+  * Removes the parent references
+  * The input *must* have been put through canonicalize_path previously.
+  */
+ static void
+ remove_parent_references(char *inpath)
+ {
+ 	char	   *path;
+ 	char	   *epath;
+ 	char	   *edestpath;
+ 	int			len;
+ 	int			i;
+ 	int			pending_strips = 0;
+ 
+ 	len = strlen(inpath);
+ 	path = malloc(len + 2);
+ 	if (!path)
+ 	{
+ 		return;
+ 	}
+ 	MemSet(path, 0, len + 2);
+ 
+ 	epath = inpath + len;
+ 	edestpath = path + len;
+ 
+ 	for (i = len; i >= 0; i--, epath--)
+ 	{
+ 		if (IS_DIR_SEP(*epath))
+ 		{
+ 			if ((i > 3) && (*(epath - 1) == '.')
+ 				&& (*(epath - 2) == '.') && IS_DIR_SEP(*(epath - 3)))
+ 			{
+ 				/* Matching "/../" - trim the parent directory */
+ 				if (!pending_strips)
+ 					pending_strips = 1;
+ 				pending_strips++;
+ 				i -= 2;
+ 				epath -= 2;
+ 			}
+ 			else if ((i > 2) && (*(epath - 1) == '.') && IS_DIR_SEP(*(epath - 2)))
+ 			{
+ 				/* Matching "/./" - can be skipped */
+ 				i -= 1;
+ 				epath -= 1;
+ 				continue;
+ 			}
+ 			else if (pending_strips)
+ 			{
+ 				pending_strips--;
+ 			}
+ 		}
+ 
+ 		if (pending_strips == 0)
+ 		{
+ 			*edestpath = *epath;
+ 			edestpath--;
+ 		}
+ 	}
+ 
+ 	if (pending_strips)
+ 	{
+ 		/* "/../", "/../../" are considered as "/" */
+ 		if (is_absolute_path(inpath))
+ 		{
+ 			*(inpath++) = '/';
+ 		}
+ 		else
+ 		{
+ 			/* Incase of relative path prefix with "../" */
+ 			while (pending_strips--)
+ 			{
+ 				strcpy(inpath, "../");
+ 				inpath += 3;
+ 			}
+ 		}
+ 	}
+ 
+ 	strcpy(inpath, edestpath + 1);
+ 	free(path);
+ 
+ 	return;
+ }
+ 
+ /*
+  * If the given pathname isn't already absolute, make it so, interpreting
+  * it relative to the current working directory.
+  */
+ static char *
+ make_absolute_path(const char *in)
+ {
+ 	char	   *result;
+ 
+ 	if (is_absolute_path(in))
+ 		result = strdup(in);
+ 	else
+ 	{
+ 		char		cwdbuf[MAXPGPATH];
+ 
+ 		if (!getcwd(cwdbuf, sizeof(cwdbuf)))
+ 		{
+ 			fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
+ 			exit(2);
+ 		}
+ 
+ 		result = malloc(strlen(cwdbuf) + strlen(in) + 2);
+ 		sprintf(result, "%s/%s", cwdbuf, in);
+ 	}
+ 
+ 	canonicalize_path(result);
+ 	remove_parent_references(result);
+ 	return result;
+ }
+ 
+ /*
+  *	This function validates the given cluster directory - we search for a
+  *	small set of subdirectories that we expect to find in a valid data directory.
+  *	directory.	If any of the subdirectories are missing (or secured against
+  *	us) we display an error message and exit()
+  *
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0)
+ 		{
+ 			fprintf(stderr, _("%s: check for \"%s\" failed: %s\n"),
+ 					progname, subDirName, strerror(errno));
+ 			return false;
+ 		}
+ 		else if (!S_ISDIR(statBuf.st_mode))
+ 		{
+ 			fprintf(stderr, _("%s: %s is not a directory.\n"), progname, subDirName);
+ 			return false;
+ 		}
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * This function validates the given path is in or below cluster directory or
+  * any one of "base", "global", or "pg_tblspc" subdirectory.
+  */
+ static bool
+ check_path_belongs_to_pgdata(const char *in_pg_data, char *in_path,
+ 							 bool *is_inside_pg_data, bool *path_are_same)
+ {
+ 	char	   *usrpath;
+ 	char	   *pg_data;
+ 	int			data_path_len;
+ 
+ 	pg_data = make_absolute_path(in_pg_data);
+ 	usrpath = make_absolute_path(in_path);
+ 
+ 	data_path_len = strlen(pg_data);
+ 
+ 	/* check input usrpath is subdirectory of pg_data usrpath */
+ 	if (strncmp(pg_data, usrpath, data_path_len) == 0)
+ 	{
+ 		if (usrpath[data_path_len] == '\0')
+ 		{
+ 			*path_are_same = true;
+ 			*is_inside_pg_data = true;
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 		else if (!IS_DIR_SEP(usrpath[data_path_len]))
+ 		{
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return false;
+ 		}
+ 
+ 		*is_inside_pg_data = true;
+ 
+ 		/* Input usrpath same as pg_data but ending with ending with DIR_SEP */
+ 		if (usrpath[data_path_len + 1] == '\0')
+ 		{
+ 			*path_are_same = true;
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 
+ 		/*
+ 		 * Input usrpath is subdirectory of pg_data usrpath, so validate the
+ 		 * remaining usrpath it should be inside any one of "base", "global",
+ 		 * "pg_tablespc"
+ 		 */
+ 		if ((path_is_prefix_of_path("base", usrpath + (data_path_len + 1)))
+ 			|| (path_is_prefix_of_path("global", usrpath + (data_path_len + 1)))
+ 			|| (path_is_prefix_of_path("pg_tblspc", usrpath + (data_path_len + 1))))
+ 		{
+ 			free(pg_data);
+ 			free(usrpath);
+ 			return true;
+ 		}
+ 	}
+ 	free(pg_data);
+ 	free(usrpath);
+ 
+ 	return false;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name, bool with_ext)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (with_ext)
+ 	{
+ 		if (name[pos] == '_')
+ 		{
+ 			pos++;
+ 			while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 				pos++;
+ 		}
+ 		if (name[pos] == '.')
+ 		{
+ 			pos++;
+ 			while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 				pos++;
+ 		}
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"path", required_argument, NULL, 'p'},
+ 		{"data-directory", no_argument, NULL, 'P'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 
+ 	int			optindex;
+ 	int			c;
+ 	char	   *DataDir;
+ 	int			fd;
+ 	char		path[MAXPGPATH];
+ 	bool		print_max_lsn = false;
+ 	bool		print_pgdata_max_lsn = false;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	int			result = 0;
+ 	bool		is_whole_data_dir = false;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "p:P", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'p':
+ 				if (print_max_lsn)
+ 				{
+ 					fprintf(stderr, _("%s: multiple -p options are not supported.\n"), progname);
+ 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 					exit(1);
+ 				}
+ 				print_max_lsn = true;
+ 				LsnSearchPath = strdup(optarg);
+ 				break;
+ 
+ 			case 'P':
+ 				if (print_pgdata_max_lsn)
+ 				{
+ 					fprintf(stderr, _("%s: multiple -P options are not supported.\n"), progname);
+ 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 					exit(1);
+ 				}
+ 				print_pgdata_max_lsn = true;
+ 				break;
+ 
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	if (print_max_lsn && print_pgdata_max_lsn)
+ 	{
+ 		fprintf(stderr, _("%s: both options -P and -p can not be combined.\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);
+ 	}
+ 
+ 	if ((optind + 1) != argc)
+ 	{
+ 		fprintf(stderr, _("%s: multiple data directories not supported.\n"), progname);
+ 		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 		exit(1);
+ 	}
+ 
+ 	/*
+ 	 * Don't allow pg_computemaxlsn 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 = strdup(argv[optind]);
+ 
+ 	canonicalize_path(DataDir);
+ 
+ 
+ 	if (!check_data_dir(DataDir))
+ 	{
+ 		fprintf(stderr, _("%s: \"%s\" not a valid  data directory.\n"),
+ 				progname, DataDir);
+ 		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)) < 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);
+ 	}
+ 
+ 	if (print_max_lsn)
+ 	{
+ 		struct stat		fst;
+ 		bool			is_inside_pg_data = false;
+ 
+ 		canonicalize_path(LsnSearchPath);
+ 
+ 		if (!check_path_belongs_to_pgdata(DataDir, LsnSearchPath, &is_inside_pg_data, &is_whole_data_dir))
+ 		{
+ 			if (is_inside_pg_data)
+ 			{
+ 				fprintf(stderr, _("%s: Path \"%s\" should be in or below any of \"base\",\"global\" or \"pg_tblspc\" of data directory.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			else
+ 			{
+ 				fprintf(stderr, _("%s: Path \"%s\" should be in or below data directory.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			exit(1);
+ 		}
+ 
+ 		if (is_whole_data_dir)
+ 		{
+ 			/* Process for whole data directory. */
+ 		}
+ 		else if (lstat(LsnSearchPath, &fst) < 0)
+ 		{
+ 			if (errno == ENOENT)
+ 			{
+ 				fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			else
+ 			{
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, LsnSearchPath, strerror(errno));
+ 			}
+ 			exit(1);
+ 		}
+ 		else if (getLinkPath(&fst, LsnSearchPath, path, sizeof(path)) > 0)
+ 		{
+ 			result = FindMaxLSNinDir(path, &maxLSN, true);
+ 		}
+ 		else if (S_ISDIR(fst.st_mode))
+ 		{
+ 			result = FindMaxLSNinDir(LsnSearchPath, &maxLSN, false);
+ 		}
+ 		else if (S_ISREG(fst.st_mode))
+ 		{
+ 			result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 		}
+ 		else
+ 		{
+ 			fprintf(stderr, _("%s: skipping special file \"%s\"\n"), progname, LsnSearchPath);
+ 		}
+ 	}
+ 
+ 	/* By default we need to compute max lsn for database */
+ 	if ((print_max_lsn == false) || is_whole_data_dir)
+ 	{
+ 		result = FindMaxLSNinPgData(DataDir, &maxLSN);
+ 	}
+ 
+ 	if (0 != result)
+ 	{
+ 		/* Message already provided, simply exit */
+ 		exit(1);
+ 	}
+ 
+ 	XLByteToSeg(maxLSN, logSegNo);
+ 
+ 	printf("Maximum LSN found is: %X/%X \nWAL segment file name (fileid,seg): %X/%X\n",
+ 		   (uint32)(maxLSN >> 32), (uint32) maxLSN,
+ 		   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * PageHeaderIsValid: Check page is valid or not
+  */
+ bool
+ PageHeaderIsValid(PageHeader page)
+ {
+ 	char	   *pagebytes;
+ 	int			i;
+ 
+ 	/* Check normal case */
+ 	if (PageGetPageSize(page) == BLCKSZ &&
+ 		PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
+ 		(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
+ 		page->pd_lower >= SizeOfPageHeaderData &&
+ 		page->pd_lower <= page->pd_upper &&
+ 		page->pd_upper <= page->pd_special &&
+ 		page->pd_special <= BLCKSZ &&
+ 		page->pd_special == MAXALIGN(page->pd_special))
+ 		return true;
+ 
+ 	/*
+ 	 * Check all-zeroes till page header; this is used only to log the page
+ 	 * details even we detect invalid page we will continue to next pages
+ 	 */
+ 	pagebytes = (char *) page;
+ 	for (i = 0; i < PAGEHDRSZ; i++)
+ 	{
+ 		if (pagebytes[i] != 0)
+ 			return false;
+ 	}
+ 	return true;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  *
+  */
+ static int
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return -1;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and validate; if we find invalid page log the
+ 	 * details of page and continue to next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to next page  \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		if (PageHeaderIsValid((PageHeader) buffer))
+ 		{
+ 			pagelsn = PageGetLSN(buffer);
+ 			if (*maxlsn < pagelsn)
+ 			{
+ 				*maxlsn = pagelsn;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			/*
+ 			 * If page is invalid log the error and continue
+ 			 */
+ 			fprintf(stderr, _("%s: Invalid page found in file \"%s\" pagid:%d\n"),
+ 					progname, filename, blocknum);
+ 		}
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 	return 0;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static int
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn, bool is_fromlink)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 	char		linkpath[MAXPGPATH];
+ 	int			result;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all the local/global temporary files, and read and read all
+ 		 * remaining relfinenode files
+ 		 */
+ 		if (is_fromlink)
+ 		{
+ 			/* If directory is link then only allow PG_* path only */
+ 			if (!validateTablespaceDir(de->d_name))
+ 				continue;
+ 		}
+ 		else if (!validateRelfilenodename(de->d_name, true))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 			{
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 			}
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		result = getLinkPath(&statbuf, pathbuf, linkpath, sizeof(linkpath));
+ 		if (result < 0)
+ 		{
+ 			continue;
+ 		}
+ 		else if (result > 0)
+ 		{
+ 			(void) FindMaxLSNinDir(linkpath, maxlsn, true);
+ 		}
+ 		else if (S_ISDIR(statbuf.st_mode))
+ 			(void) FindMaxLSNinDir(pathbuf, maxlsn, false);
+ 		else
+ 			(void) FindMaxLSNinFile(pathbuf, maxlsn);
+ 	}
+ 
+ 	closedir(dir);
+ 	return 0;
+ }
+ 
+ /*
+  * Get the link path.
+  * On success, returns length of link filename.
+  * and return zero in case of file is not a link type.
+  * On failure, returns -1.
+  */
+ static int
+ getLinkPath(struct stat * statbuf, char *path, char *linkpath, int length)
+ {
+ 	int			rllen;
+ 
+ 	if (
+ #ifndef WIN32
+ 		S_ISLNK(statbuf->st_mode)
+ #else
+ 		pgwin32_is_junction(path)
+ #endif
+ 		)
+ 	{
+ #if defined(HAVE_READLINK) || defined(WIN32)
+ 
+ 		rllen = readlink(path, linkpath, length);
+ 		if (rllen < 0)
+ 		{
+ 			fprintf(stderr, _("%s: could not read symbolic link \"%s\", so skipping file.\n"),
+ 					progname, path);
+ 			return -1;
+ 		}
+ 
+ 		if (rllen >= length)
+ 		{
+ 			fprintf(stderr, _("%s: symbolic link \"%s\" target is too long, so skipping file.\n"),
+ 					progname, path);
+ 
+ 			return -1;
+ 		}
+ 
+ 		linkpath[rllen] = '\0';
+ 
+ 		return rllen;
+ #else
+ 		/* tablespaces are not supported on this platform */
+ 		return -1;
+ #endif   /* HAVE_READLINK */
+ 	}
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static int
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false))
+ 		return -1;
+ 
+ 	return 0;
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s [OPTION]... DATADIR\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -p, --path=FILE_FOLDER_PATH   print max LSN from file or directory name\n"));
+ 	printf(_("  -P, --data-directory          print max LSN from whole data directory\n"));
+ 	printf(_("  -V, --version                 output version information, then exit\n"));
+ 	printf(_("  -?, --help                    show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
***************
*** 177,182 **** Complete list of usable sgml source files in this directory.
--- 177,183 ----
  <!ENTITY pgDumpall          SYSTEM "pg_dumpall.sgml">
  <!ENTITY pgReceivexlog      SYSTEM "pg_receivexlog.sgml">
  <!ENTITY pgResetxlog        SYSTEM "pg_resetxlog.sgml">
+ <!ENTITY pgComputemaxlsn    SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgRestore          SYSTEM "pg_restore.sgml">
  <!ENTITY postgres           SYSTEM "postgres-ref.sgml">
  <!ENTITY postmaster         SYSTEM "postmaster.sgml">
*** /dev/null
--- b/doc/src/sgml/ref/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,79 ----
+ <!--
+ doc/src/sgml/ref/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="APP-PGCOMPUTEMAXLSN">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="app-pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><option>-P</option></arg>
+    <arg choice="opt"><option>-p</option> <replaceable class="parameter">file-name</replaceable> | <replaceable class="parameter">folder-name</replaceable></arg>
+    <arg choice="plain"><replaceable>datadir</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages.
+   </para>
+ 
+   <para>
+    This utility can only be run by the user who installed the server, because
+    it requires read/write access to the data directory.
+    For safety reasons, you must specify the data directory on the command line.
+    <command>pg_computemaxlsn</command> does not use the environment variable
+    <envar>PGDATA</>.
+   </para>
+ 
+   <para>
+    The <option>-P</> or <option>--data-directory</> for computing maximum LSN from all the pages in data directory.
+    This is the default option if none of the options are provided.
+   </para>
+ 
+   <para>
+    The <option>-p <replaceable class="parameter">file-name | folder-name</replaceable></option> or
+    <option>--path=<replaceable class="parameter">file-name | folder-name</replaceable></option> for computing
+    maximun LSN from specific file or folder. File or folder path should be in or below the data directory.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options print
+    the <application>pg_computemaxlsn</application> version and exit.  The
+    options <option>-?</> and <option>--help</> show supported arguments,
+    and exit.
+   </para>
+ 
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+ 
+   <para>
+    This command must not be used when the server is
+    running.  <command>pg_computemaxlsn</command> will refuse to start up if
+    it finds a server lock file in the data directory.  If the
+    server crashed then a lock file might have been left
+    behind; in that case you can remove the lock file to allow
+    <command>pg_computemaxlsn</command> to run.  But before you do
+    so, make doubly certain that there is no server process still alive.
+   </para>
+  </refsect1>
+ 
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 135,140 **** PostgreSQL documentation
--- 135,150 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       with <option>-P</> option for finding maximum LSN from the data directory or 
+       for from specific file or folder <option>-p <filename>file-name | folder-name</></>.
+       Once larger WAL segment file is found use <option>-l</> option for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 248,253 ****
--- 248,254 ----
     &pgControldata;
     &pgCtl;
     &pgResetxlog;
+    &pgComputemaxlsn;
     &postgres;
     &postmaster;
  
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 34,40 **** my @contrib_uselibpgport = (
  	'oid2name',      'pgbench',
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
! 	'pg_upgrade',    'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 34,40 ----
  	'oid2name',      'pgbench',
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
! 	'pg_upgrade',    'vacuumlo', 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#6Josh Berkus
josh@agliodbs.com
In reply to: Amit kapila (#5)

Hackers,

Amit posted a new version of this patch on January 23rd. But last
comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Andres Freund
andres@2ndquadrant.com
In reply to: Josh Berkus (#6)

Hi,

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But last
comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I better
explain:

I think the usecase for this utility isn't big enough to be included in
postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff it's
not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to find
the biggest lsn used so far so the user then can use pg_resetxlog to set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8Amit Kapila
amit.kapila@huawei.com
In reply to: Andres Freund (#7)

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

Hi,

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But last
comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I better
explain:

I think the usecase for this utility isn't big enough to be included in
postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff it's
not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to
find
the biggest lsn used so far so the user then can use pg_resetxlog to
set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

One of the main reason this was written was to make server up in case of
corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information which he
wants to take dump.
So I think in such scenario's it can be quite helpful to users, but such
scenarios are rare and so
it might not be a utility which user will need often.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Andres Freund
andres@2ndquadrant.com
In reply to: Amit Kapila (#8)

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But last
comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I better
explain:

I think the usecase for this utility isn't big enough to be included in
postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff it's
not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to
find
the biggest lsn used so far so the user then can use pg_resetxlog to
set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

One of the main reason this was written was to make server up in case of
corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information which he
wants to take dump.

Which information would that loose? We don't currently use the LSN for
tuple visibility. And you sure wouldn't do anything but dump such a
cluster.
Now you could argue that you could modify this to find the current xid
used - but that's not that easy due to the wraparound semantics of
xids. And you are more likely to be successfull by looking at pg_clog.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Amit Kapila
amit.kapila@huawei.com
In reply to: Andres Freund (#9)

On Wednesday, June 26, 2013 1:20 PM Andres Freund wrote:

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But

last

comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I better
explain:

I think the usecase for this utility isn't big enough to be

included in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to
find
the biggest lsn used so far so the user then can use pg_resetxlog

to

set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

One of the main reason this was written was to make server up in case

of

corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information which

he

wants to take dump.

Which information would that loose?

Information from WAL replay which can be more appropriate by selecting
LSN.
Also for a developer, guessing very high LSN might be easy, but for users
it might not be equally easy, and getting such value by utility would be
comfortable.

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master) whether a full
backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the moment
of the failover.

We don't currently use the LSN for
tuple visibility. And you sure wouldn't do anything but dump such a
cluster.
Now you could argue that you could modify this to find the current xid
used - but that's not that easy due to the wraparound semantics of
xids. And you are more likely to be successfull by looking at pg_clog.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Andres Freund
andres@2ndquadrant.com
In reply to: Amit Kapila (#10)

Hi Amit,

On 2013-06-26 16:22:28 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 1:20 PM Andres Freund wrote:

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But

last

comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I better
explain:

I think the usecase for this utility isn't big enough to be

included in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to
find
the biggest lsn used so far so the user then can use pg_resetxlog

to

set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

One of the main reason this was written was to make server up in case

of

corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information which

he

wants to take dump.

Which information would that loose?

Information from WAL replay which can be more appropriate by selecting
LSN.

Sorry, I can't follow. If wal replay still is an option you can just
look at the WAL and get a sensible value way easier. The whole tool
seems to only make sense if you've lost pg_xlog.

Also for a developer, guessing very high LSN might be easy, but for users
it might not be equally easy, and getting such value by utility would be
comfortable.

Well, then we can just document some very high lsn and be done with
it. Like CF000000/00000000.
That would leave enough space for eventual writes caused while dumping
the database (say hint bit writes in a checksummed database) and cannot
yet be realistically be reached during normal operation.

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master) whether a full
backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the moment
of the failover.

That's exactly what I was afraid of. Unless I miss something the tool is
*NOT* sufficient to do this. Look at the mail introducing pg_rewind and
the ensuing discussion for what's necessary for that.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Amit Kapila
amit.kapila@huawei.com
In reply to: Andres Freund (#11)

On Wednesday, June 26, 2013 4:40 PM Andres Freund wrote:

Hi Amit,

On 2013-06-26 16:22:28 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 1:20 PM Andres Freund wrote:

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But

last

comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I

better

explain:

I think the usecase for this utility isn't big enough to be

included in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for

stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be

used to

find
the biggest lsn used so far so the user then can use

pg_resetxlog

to

set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN

to

something very, very high. The database cannot be used for

anything

reliable afterwards anyway.

One of the main reason this was written was to make server up in

case

of

corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information

which

he

wants to take dump.

Which information would that loose?

Information from WAL replay which can be more appropriate by

selecting

LSN.

Sorry, I can't follow. If wal replay still is an option you can just
look at the WAL and get a sensible value way easier.

Originally 2 parts were proposed, one was to get LSN from data pages and
other from data pages.
Original proposal is:
/messages/by-id/6C0B27F7206C9E4CA54AE035729E9C382851FFA
1@szxeml509-mbs

The second part for looking into WAL was written but due to xlogreader
patch, it was postponed and I didn't get time after that
to pursue it.

The whole tool
seems to only make sense if you've lost pg_xlog.

The tool's initial intent was if pg_controldata is lost and this idea is
originated in below mail chain:
/messages/by-id/4274.1340084598@sss.pgh.pa.us

Also for a developer, guessing very high LSN might be easy, but for

users

it might not be equally easy, and getting such value by utility

would be

comfortable.

Well, then we can just document some very high lsn and be done with
it. Like CF000000/00000000.
That would leave enough space for eventual writes caused while dumping
the database (say hint bit writes in a checksummed database) and cannot
yet be realistically be reached during normal operation.

Can we be ultra sure, that this LSN is not reached. I think it will take
vary long to reach such LSN, but still theoretically it can be possible.
I don't have any evidence.

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master) whether

a full

backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the

moment

of the failover.

That's exactly what I was afraid of. Unless I miss something the tool
is
*NOT* sufficient to do this.

You mean to say if user knows the max LSN of data pages in old-master and
last applied LSN in new master, he cannot decide whether
Full backup is needed? It should be straightforward decision that skip a
backup if that old-master LSN is less than the new-master (i.e., last
applied LSN, IOW, timeline switch LSN).
It was proposed as a usecase in this below mail:
/messages/by-id/CAHGQGwHyd1fY0hF0qKh0-uKDh-gcbYxMOFBYVk
Kh4jzji-FCfg@mail.gmail.com

Look at the mail introducing pg_rewind and
the ensuing discussion for what's necessary for that.

I had briefly looked into that discussion at the time it was going on, but I
will look into it more carefully.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Fujii Masao
masao.fujii@gmail.com
In reply to: Amit kapila (#1)

On Wed, Jun 26, 2013 at 8:57 PM, Amit Kapila <amit.kapila@huawei.com> wrote:

On Wednesday, June 26, 2013 4:40 PM Andres Freund wrote:

Hi Amit,

On 2013-06-26 16:22:28 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 1:20 PM Andres Freund wrote:

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd. But

last

comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I

better

explain:

I think the usecase for this utility isn't big enough to be

included in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for

stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be

used to

find
the biggest lsn used so far so the user then can use

pg_resetxlog

to

set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN

to

something very, very high. The database cannot be used for

anything

reliable afterwards anyway.

One of the main reason this was written was to make server up in

case

of

corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the information

which

he

wants to take dump.

Which information would that loose?

Information from WAL replay which can be more appropriate by

selecting

LSN.

Sorry, I can't follow. If wal replay still is an option you can just
look at the WAL and get a sensible value way easier.

Originally 2 parts were proposed, one was to get LSN from data pages and
other from data pages.
Original proposal is:
/messages/by-id/6C0B27F7206C9E4CA54AE035729E9C382851FFA
1@szxeml509-mbs

The second part for looking into WAL was written but due to xlogreader
patch, it was postponed and I didn't get time after that
to pursue it.

The whole tool
seems to only make sense if you've lost pg_xlog.

The tool's initial intent was if pg_controldata is lost and this idea is
originated in below mail chain:
/messages/by-id/4274.1340084598@sss.pgh.pa.us

Also for a developer, guessing very high LSN might be easy, but for

users

it might not be equally easy, and getting such value by utility

would be

comfortable.

Well, then we can just document some very high lsn and be done with
it. Like CF000000/00000000.
That would leave enough space for eventual writes caused while dumping
the database (say hint bit writes in a checksummed database) and cannot
yet be realistically be reached during normal operation.

Can we be ultra sure, that this LSN is not reached. I think it will take
vary long to reach such LSN, but still theoretically it can be possible.
I don't have any evidence.

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master) whether

a full

backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the

moment

of the failover.

That's exactly what I was afraid of. Unless I miss something the tool
is
*NOT* sufficient to do this.

You mean to say if user knows the max LSN of data pages in old-master and
last applied LSN in new master, he cannot decide whether
Full backup is needed? It should be straightforward decision that skip a
backup if that old-master LSN is less than the new-master (i.e., last
applied LSN, IOW, timeline switch LSN).
It was proposed as a usecase in this below mail:
/messages/by-id/CAHGQGwHyd1fY0hF0qKh0-uKDh-gcbYxMOFBYVk
Kh4jzji-FCfg@mail.gmail.com

I guess he meant the commit hint bit problem.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Amit Kapila
amit.kapila@huawei.com
In reply to: Fujii Masao (#13)

On Wednesday, June 26, 2013 10:19 PM Fujii Masao wrote:

On Wed, Jun 26, 2013 at 8:57 PM, Amit Kapila <amit.kapila@huawei.com>
wrote:

On Wednesday, June 26, 2013 4:40 PM Andres Freund wrote:

Hi Amit,

On 2013-06-26 16:22:28 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 1:20 PM Andres Freund wrote:

On 2013-06-26 08:50:27 +0530, Amit Kapila wrote:

On Tuesday, June 25, 2013 11:12 PM Andres Freund wrote:

On 2013-06-16 17:19:49 -0700, Josh Berkus wrote:

Amit posted a new version of this patch on January 23rd.

But

last

comment on it by Tom is "not sure everyone wants this".

https://commitfest.postgresql.org/action/patch_view?id=905

... so, what's the status of this patch?

That comment was referencing a mail of mine - so perhaps I

better

explain:

I think the usecase for this utility isn't big enough to be

included in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for

stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be

used to

find
the biggest lsn used so far so the user then can use

pg_resetxlog

to

set
that as the wal starting point.
But that can be way much easier solved by just setting the

LSN

to

something very, very high. The database cannot be used for

anything

reliable afterwards anyway.

One of the main reason this was written was to make server up

in

case

of

corruption and
user can take dump of some useful information if any.

By setting LSN very, very high user might loose the

information

which

he

wants to take dump.

Which information would that loose?

Information from WAL replay which can be more appropriate by

selecting

LSN.

Sorry, I can't follow. If wal replay still is an option you can just
look at the WAL and get a sensible value way easier.

Originally 2 parts were proposed, one was to get LSN from data pages

and

other from data pages.
Original proposal is:
http://www.postgresql.org/message-

id/6C0B27F7206C9E4CA54AE035729E9C382851FFA

1@szxeml509-mbs

The second part for looking into WAL was written but due to

xlogreader

patch, it was postponed and I didn't get time after that
to pursue it.

The whole tool
seems to only make sense if you've lost pg_xlog.

The tool's initial intent was if pg_controldata is lost and this idea

is

originated in below mail chain:
/messages/by-id/4274.1340084598@sss.pgh.pa.us

Also for a developer, guessing very high LSN might be easy, but

for

users

it might not be equally easy, and getting such value by utility

would be

comfortable.

Well, then we can just document some very high lsn and be done with
it. Like CF000000/00000000.
That would leave enough space for eventual writes caused while

dumping

the database (say hint bit writes in a checksummed database) and

cannot

yet be realistically be reached during normal operation.

Can we be ultra sure, that this LSN is not reached. I think it will

take

vary long to reach such LSN, but still theoretically it can be

possible.

I don't have any evidence.

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master)

whether

a full

backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the

moment

of the failover.

That's exactly what I was afraid of. Unless I miss something the

tool

is
*NOT* sufficient to do this.

You mean to say if user knows the max LSN of data pages in old-master

and

last applied LSN in new master, he cannot decide whether
Full backup is needed? It should be straightforward decision that

skip a

backup if that old-master LSN is less than the new-master (i.e., last
applied LSN, IOW, timeline switch LSN).
It was proposed as a usecase in this below mail:
/messages/by-id/CAHGQGwHyd1fY0hF0qKh0-uKDh-

gcbYxMOFBYVk

Kh4jzji-FCfg@mail.gmail.com

I guess he meant the commit hint bit problem.

True, after reading the thread mentioned by Andres, I got the reason he was
pointing why it is not sufficient.
So can it be useful if database has checksums enabled?

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15'Andres Freund'
andres@2ndquadrant.com
In reply to: Amit Kapila (#14)

On 2013-06-27 11:16:25 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 10:19 PM Fujii Masao wrote:

On Wed, Jun 26, 2013 at 8:57 PM, Amit Kapila <amit.kapila@huawei.com>

One more use case for which this utility was done is as below:
It will be used to decide that on new-standby (old-master)

whether

a full

backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master precedes
the last applied LSN in old-standby (i.e., new-master) at the

moment

of the failover.

That's exactly what I was afraid of. Unless I miss something the

tool

is
*NOT* sufficient to do this.

You mean to say if user knows the max LSN of data pages in old-master

and

last applied LSN in new master, he cannot decide whether
Full backup is needed? It should be straightforward decision that

skip a

backup if that old-master LSN is less than the new-master (i.e., last
applied LSN, IOW, timeline switch LSN).
It was proposed as a usecase in this below mail:
/messages/by-id/CAHGQGwHyd1fY0hF0qKh0-uKDh-

gcbYxMOFBYVk

Kh4jzji-FCfg@mail.gmail.com

I guess he meant the commit hint bit problem.

True, after reading the thread mentioned by Andres, I got the reason he was
pointing why it is not sufficient.
So can it be useful if database has checksums enabled?

I think for that usecase its far more useful to work on getting
pg_rewind since that has a chance of working when local WAL has been
applied that hadn't yet shipped to the other side (which is frequently
the case).

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16Amit Kapila
amit.kapila@huawei.com
In reply to: 'Andres Freund' (#15)

On Thursday, June 27, 2013 11:26 AM Andres Freund wrote:

On 2013-06-27 11:16:25 +0530, Amit Kapila wrote:

On Wednesday, June 26, 2013 10:19 PM Fujii Masao wrote:

On Wed, Jun 26, 2013 at 8:57 PM, Amit Kapila

<amit.kapila@huawei.com>

One more use case for which this utility was done is as

below:

It will be used to decide that on new-standby (old-master)

whether

a full

backup is needed from
New-master(old-standby).
The backup is required when the data page in old-master

precedes

the last applied LSN in old-standby (i.e., new-master) at

the

moment

of the failover.

That's exactly what I was afraid of. Unless I miss something the

tool

is
*NOT* sufficient to do this.

You mean to say if user knows the max LSN of data pages in old-

master

and

last applied LSN in new master, he cannot decide whether
Full backup is needed? It should be straightforward decision that

skip a

backup if that old-master LSN is less than the new-master (i.e.,

last

applied LSN, IOW, timeline switch LSN).
It was proposed as a usecase in this below mail:
/messages/by-id/CAHGQGwHyd1fY0hF0qKh0-uKDh-

gcbYxMOFBYVk

Kh4jzji-FCfg@mail.gmail.com

I guess he meant the commit hint bit problem.

True, after reading the thread mentioned by Andres, I got the reason

he was

pointing why it is not sufficient.
So can it be useful if database has checksums enabled?

I think for that usecase its far more useful to work on getting
pg_rewind since that has a chance of working when local WAL has been
applied that hadn't yet shipped to the other side (which is frequently
the case).

Aren't the use case for both is bit different
Pg_computmaxlsn - by computing max lsn for checksums enabled database, user
can made old-master follow new-master if maxlsn suggests that fullbackup is
not required.

Pg_rewind - a tool to resynchronize old-master and new-master by
copying changed blocks from new master.
I think more work might be needed in case DDL's happened
on old-master after forking of new-master.

Although for this case, both have resemblance in terms of avoiding full
backup, but I think maxlsn tool can be independently also used.
Do you think pg_rewind can be used by default for any checksum enabled
database to resynchronize old-master?

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Robert Haas
robertmhaas@gmail.com
In reply to: Andres Freund (#7)

On Tue, Jun 25, 2013 at 1:42 PM, Andres Freund <andres@2ndquadrant.com> wrote:

I think the usecase for this utility isn't big enough to be included in
postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff it's
not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to find
the biggest lsn used so far so the user then can use pg_resetxlog to set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

I guess this is true, but I think I'm mildly in favor of including
this anyway. I think I would have used it once or twice, if it had
been there - maybe not even to feed into pg_resetxlog, but just to
check for future LSNs. We don't have anything like a suite of
diagnostic tools in bin or contrib today, for use by database
professionals in fixing things that fall strictly in the realm of
"don't try this at home", and I think we should have such a thing.
Unfortunately this covers about 0.1% of the space I'd like to see
covered, which might be a reason to reject it or at least insist that
it be enhanced first.

At any rate, I don't think this is anywhere near committable as it
stands today. Some random review comments:

remove_parent_refernces() is spelled wrong.

Why does this patch need all of this fancy path-handling logic -
specifically, remove_parent_refernces() and make_absolute_path()?
Surely its needs are not that different from pg_ctl or pg_resetxlog or
pg_controldata. If they all need it and it's duplicated, we should
fix that. Otherwise, why the special logic here?

I don't think we need getLinkPath() either. The server has no trouble
finding its files by just using a pathname that follows the symlink.
Why do we need anything different here? The whole point of symlinks
is that you can traverse them transparently, without knowing where
they point.

Duplicating PageHeaderIsValid doesn't seem acceptable. Moreover,
since the point of this is to be able to use it on a damaged cluster,
why is that logic even desirable? I think we'd be better off assuming
the pages to be valid.

The calling convention for this utility seems quite baroque. There's
no real need for all of this -p/-P stuff. I think the syntax should
just be:

pg_computemaxlsn file_or_directory...

For each argument, we determine whether it's a file or a directory.
If it's a file, we assume it's a PostgreSQL data file and scan it. If
it's a directory, we check whether it looks like a data directory. If
it does, we recurse through the whole tree structure and find the data
files, and process them. If it doesn't look like a data directory, we
scan each plain file in that directory whose name looks like a
PostgreSQL data file name. With this approach, there's no need to
limit ourselves to a single input argument and no need to specify what
kind of argument it is; the tool just figures it out.

I think it would be a good idea to have a mode that prints out the max
LSN found in *each data file* scanned, and then prints out the overall
max found at the end. In fact, I think that should perhaps be the
default mode, with -q, --quiet to disable it. When printing out the
per-file data, I think it would be useful to track and print the block
number where the highest LSN in that file was found. I have
definitely had cases where I suspected, but was not certain of,
corruption. One could use a tool like this to hunt for problems, and
then use something like pg_filedump to dump the offending blocks.
That would be a lot easier than running pg_filedump on the whole file
and grepping through the output.

Similarly, I see no reason for the restriction imposed by
check_path_belongs_to_pgdata(). I've had people mail me one data
file; why shouldn't I be able to run this tool on it? It's a
read-only utility.

- if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false)) and similar is not
PostgreSQL style.

+ printf(_("%s compute the maximum LSN in PostgreSQL data
pages.\n\n"), progname);

s/compute/computes/

+       /*
+        * Don't allow pg_computemaxlsn 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

This utility only reads files; it does not modify them. So this seems
unnecessary. I assume it's blindly copied from somewhere else.

+ fprintf(stderr, _("%s: \"%s\" not a valid data directory.\n"),

Extra space.

+       /*
+        * 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);

Again, this might be appropriate for pg_resetxlog, but I see no reason
for the restriction here. The output might be inaccurate in that
case, but if you're using this tool you're required to know what
you're doing.

+    For safety reasons, you must specify the data directory on the
command line.
+    <command>pg_computemaxlsn</command> does not use the environment variable
+    <envar>PGDATA</>.

Same thing here. I think using PGDATA would be quite appropriate for
this utility.

+   <para>
+    This command must not be used when the server is
+    running.  <command>pg_computemaxlsn</command> will refuse to start up if
+    it finds a server lock file in the data directory.  If the
+    server crashed then a lock file might have been left
+    behind; in that case you can remove the lock file to allow
+    <command>pg_computemaxlsn</command> to run.  But before you do
+    so, make doubly certain that there is no server process still alive.
+   </para>

More of the same paranoia.

Overall my feeling is that this can be simplified quite a lot.
There's a lot of stuff in here that's really not needed.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Amit Kapila
amit.kapila@huawei.com
In reply to: Robert Haas (#17)

On Wednesday, July 03, 2013 1:26 AM Robert Haas wrote:

On Tue, Jun 25, 2013 at 1:42 PM, Andres Freund <andres@2ndquadrant.com>
wrote:

I think the usecase for this utility isn't big enough to be included

in

postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff

it's

not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to

find

the biggest lsn used so far so the user then can use pg_resetxlog to

set

that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

I guess this is true, but I think I'm mildly in favor of including
this anyway. I think I would have used it once or twice, if it had
been there - maybe not even to feed into pg_resetxlog, but just to
check for future LSNs. We don't have anything like a suite of
diagnostic tools in bin or contrib today, for use by database
professionals in fixing things that fall strictly in the realm of
"don't try this at home", and I think we should have such a thing.
Unfortunately this covers about 0.1% of the space I'd like to see
covered, which might be a reason to reject it or at least insist that
it be enhanced first.

At any rate, I don't think this is anywhere near committable as it
stands today. Some random review comments:

remove_parent_refernces() is spelled wrong.

It was corrected in last posted version.
/messages/by-id/6C0B27F7206C9E4CA54AE035729E9C383BEB928
8@szxeml509-mbx

Why does this patch need all of this fancy path-handling logic -
specifically, remove_parent_refernces() and make_absolute_path()?
Surely its needs are not that different from pg_ctl or pg_resetxlog or
pg_controldata. If they all need it and it's duplicated, we should
fix that. Otherwise, why the special logic here?

I don't think we need getLinkPath() either. The server has no trouble
finding its files by just using a pathname that follows the symlink.
Why do we need anything different here? The whole point of symlinks
is that you can traverse them transparently, without knowing where
they point.

It is to handle negative scenario's like if there is any recursion in path.
However if you feel this is not important, it can be removed.

Duplicating PageHeaderIsValid doesn't seem acceptable. Moreover,
since the point of this is to be able to use it on a damaged cluster,
why is that logic even desirable? I think we'd be better off assuming
the pages to be valid.

The calling convention for this utility seems quite baroque. There's
no real need for all of this -p/-P stuff. I think the syntax should
just be:

pg_computemaxlsn file_or_directory...

For each argument, we determine whether it's a file or a directory.
If it's a file, we assume it's a PostgreSQL data file and scan it. If
it's a directory, we check whether it looks like a data directory. If
it does, we recurse through the whole tree structure and find the data
files, and process them. If it doesn't look like a data directory, we
scan each plain file in that directory whose name looks like a
PostgreSQL data file name. With this approach, there's no need to
limit ourselves to a single input argument and no need to specify what
kind of argument it is; the tool just figures it out.

I think it would be a good idea to have a mode that prints out the max
LSN found in *each data file* scanned, and then prints out the overall
max found at the end. In fact, I think that should perhaps be the
default mode, with -q, --quiet to disable it.

Printing too many LSN for each file might fill user's screen and he might be
needing only overall LSN.
Should we keep -p --printall as option to print all LSN's and keep default
as overall max LSN?

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#19Robert Haas
robertmhaas@gmail.com
In reply to: Amit kapila (#1)

On Wed, Jul 3, 2013 at 8:44 AM, Amit Kapila <amit.kapila@huawei.com> wrote:

Why does this patch need all of this fancy path-handling logic -
specifically, remove_parent_refernces() and make_absolute_path()?
Surely its needs are not that different from pg_ctl or pg_resetxlog or
pg_controldata. If they all need it and it's duplicated, we should
fix that. Otherwise, why the special logic here?

I don't think we need getLinkPath() either. The server has no trouble
finding its files by just using a pathname that follows the symlink.
Why do we need anything different here? The whole point of symlinks
is that you can traverse them transparently, without knowing where
they point.

It is to handle negative scenario's like if there is any recursion in path.
However if you feel this is not important, it can be removed.

I'm having a hard time imagining a situation where that would be a
problem. If the symlink points to itself somehow, the OS will throw
an error. If your filesystem is so badly hosed that the directory
structure is more fundamentally broken than the OS's circular-symlink
detection code can handle, whether or not this utility works is a
second-order consideration. What kind of scenario are you imagining?

I think it would be a good idea to have a mode that prints out the max
LSN found in *each data file* scanned, and then prints out the overall
max found at the end. In fact, I think that should perhaps be the
default mode, with -q, --quiet to disable it.

Printing too many LSN for each file might fill user's screen and he might be
needing only overall LSN.
Should we keep -p --printall as option to print all LSN's and keep default
as overall max LSN?

Honestly, I have a hard time imagining the use case for that mode.
This isn't a tool that people should be running regularly, and some
output that lends a bit of confidence that the tool is doing the right
thing seems like a good thing. Keep in mind it's likely to run for
quite a while, too, and this would provide a progress indicator. I'll
defer to whatever the consensus is here but my gut feeling is that if
you don't want that extra output, there's a good chance you're
misusing the tool.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20Amit Kapila
amit.kapila@huawei.com
In reply to: Robert Haas (#19)

-----Original Message-----
From: Robert Haas [mailto:robertmhaas@gmail.com]
Sent: Wednesday, July 03, 2013 6:40 PM
To: Amit Kapila
Cc: Andres Freund; Josh Berkus; pgsql-hackers@postgresql.org
Subject: Re: [HACKERS] Review: Patch to compute Max LSN of Data Pages

On Wed, Jul 3, 2013 at 8:44 AM, Amit Kapila <amit.kapila@huawei.com>
wrote:

Why does this patch need all of this fancy path-handling logic -
specifically, remove_parent_refernces() and make_absolute_path()?
Surely its needs are not that different from pg_ctl or pg_resetxlog

or

pg_controldata. If they all need it and it's duplicated, we should
fix that. Otherwise, why the special logic here?

I don't think we need getLinkPath() either. The server has no

trouble

finding its files by just using a pathname that follows the symlink.
Why do we need anything different here? The whole point of symlinks
is that you can traverse them transparently, without knowing where
they point.

It is to handle negative scenario's like if there is any recursion in

path.

However if you feel this is not important, it can be removed.

I'm having a hard time imagining a situation where that would be a
problem. If the symlink points to itself somehow, the OS will throw
an error. If your filesystem is so badly hosed that the directory
structure is more fundamentally broken than the OS's circular-symlink
detection code can handle, whether or not this utility works is a
second-order consideration. What kind of scenario are you imagining?

amit@linux:~> md test
amit@linux:~> cd test
amit@linux:~/test> ln -s ~/test link_test
amit@linux:~/test> ls
link_test
amit@linux:~/test> cd link_test
amit@linux:~/test/link_test> ls
link_test
amit@linux:~/test/link_test> cd link_test
amit@linux:~/test/link_test/link_test> cd link_test
amit@linux:~/test/link_test/link_test/link_test> pwd
/home/amit/test/link_test/link_test/link_test
amit@linux:~/test/link_test/link_test/link_test>

Platform details
----------------
Suse - 11.2
Kernel - 3.0.13

This is to avoid when user has given some path where db files are present.

I think it would be a good idea to have a mode that prints out the

max

LSN found in *each data file* scanned, and then prints out the

overall

max found at the end. In fact, I think that should perhaps be the
default mode, with -q, --quiet to disable it.

Printing too many LSN for each file might fill user's screen and he

might be

needing only overall LSN.
Should we keep -p --printall as option to print all LSN's and keep

default

as overall max LSN?

Honestly, I have a hard time imagining the use case for that mode.
This isn't a tool that people should be running regularly, and some
output that lends a bit of confidence that the tool is doing the right
thing seems like a good thing. Keep in mind it's likely to run for
quite a while, too, and this would provide a progress indicator. I'll
defer to whatever the consensus is here but my gut feeling is that if
you don't want that extra output, there's a good chance you're
misusing the tool.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#21Robert Haas
robertmhaas@gmail.com
In reply to: Amit kapila (#1)

On Wed, Jul 3, 2013 at 9:51 AM, Amit Kapila <amit.kapila@huawei.com> wrote:

amit@linux:~> md test
amit@linux:~> cd test
amit@linux:~/test> ln -s ~/test link_test
amit@linux:~/test> ls
link_test
amit@linux:~/test> cd link_test
amit@linux:~/test/link_test> ls
link_test
amit@linux:~/test/link_test> cd link_test
amit@linux:~/test/link_test/link_test> cd link_test
amit@linux:~/test/link_test/link_test/link_test> pwd
/home/amit/test/link_test/link_test/link_test
amit@linux:~/test/link_test/link_test/link_test>

So what?

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#22Amit Kapila
amit.kapila@huawei.com
In reply to: Robert Haas (#21)

On Wednesday, July 03, 2013 7:41 PM Robert Haas wrote:

On Wed, Jul 3, 2013 at 9:51 AM, Amit Kapila <amit.kapila@huawei.com>
wrote:

amit@linux:~> md test
amit@linux:~> cd test
amit@linux:~/test> ln -s ~/test link_test
amit@linux:~/test> ls
link_test
amit@linux:~/test> cd link_test
amit@linux:~/test/link_test> ls
link_test
amit@linux:~/test/link_test> cd link_test
amit@linux:~/test/link_test/link_test> cd link_test
amit@linux:~/test/link_test/link_test/link_test> pwd
/home/amit/test/link_test/link_test/link_test
amit@linux:~/test/link_test/link_test/link_test>

So what?

It can cause error "too many levels of symbolic links"

Point was that in case of symlinks we only want to allow PG_ paths, so that
such situation can never occur.
However I think this is not important to handle by this utility, so we can
remove such handling.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#23Hari Babu
haribabu.kommi@huawei.com
In reply to: Robert Haas (#17)
1 attachment(s)

On Wednesday, July 03, 2013 1:26 AM Robert Haas Wrote:

On Tue, Jun 25, 2013 at 1:42 PM, Andres Freund <andres@2ndquadrant.com>

wrote:

I think the usecase for this utility isn't big enough to be included in
postgres since it really can only help in a very limited
circumstances. And I think it's too likely to be misused for stuff it's
not useable for (e.g. remastering).

The only scenario I see is that somebody deleted/corrupted
pg_controldata. In that scenario the tool is supposed to be used to find
the biggest lsn used so far so the user then can use pg_resetxlog to set
that as the wal starting point.
But that can be way much easier solved by just setting the LSN to
something very, very high. The database cannot be used for anything
reliable afterwards anyway.

I guess this is true, but I think I'm mildly in favor of including
this anyway. I think I would have used it once or twice, if it had
been there - maybe not even to feed into pg_resetxlog, but just to
check for future LSNs. We don't have anything like a suite of
diagnostic tools in bin or contrib today, for use by database
professionals in fixing things that fall strictly in the realm of
"don't try this at home", and I think we should have such a thing.
Unfortunately this covers about 0.1% of the space I'd like to see
covered, which might be a reason to reject it or at least insist that
it be enhanced first.

At any rate, I don't think this is anywhere near committable as it
stands today. Some random review comments:

Thanks for the detailed review.

remove_parent_refernces() is spelled wrong.

Why does this patch need all of this fancy path-handling logic -
specifically, remove_parent_refernces() and make_absolute_path()?
Surely its needs are not that different from pg_ctl or pg_resetxlog or
pg_controldata. If they all need it and it's duplicated, we should
fix that. Otherwise, why the special logic here?

I don't think we need getLinkPath() either. The server has no trouble
finding its files by just using a pathname that follows the symlink.
Why do we need anything different here? The whole point of symlinks
is that you can traverse them transparently, without knowing where
they point.

Removed the special handling of path functions.

Duplicating PageHeaderIsValid doesn't seem acceptable. Moreover,
since the point of this is to be able to use it on a damaged cluster,
why is that logic even desirable? I think we'd be better off assuming
the pages to be valid.

Corrected.

The calling convention for this utility seems quite baroque. There's
no real need for all of this -p/-P stuff. I think the syntax should
just be:

pg_computemaxlsn file_or_directory...

For each argument, we determine whether it's a file or a directory.
If it's a file, we assume it's a PostgreSQL data file and scan it. If
it's a directory, we check whether it looks like a data directory. If
it does, we recurse through the whole tree structure and find the data
files, and process them. If it doesn't look like a data directory, we
scan each plain file in that directory whose name looks like a
PostgreSQL data file name. With this approach, there's no need to
limit ourselves to a single input argument and no need to specify what
kind of argument it is; the tool just figures it out.

Changed to accept file or directory without of options.

I think it would be a good idea to have a mode that prints out the max
LSN found in *each data file* scanned, and then prints out the overall
max found at the end. In fact, I think that should perhaps be the
default mode, with -q, --quiet to disable it. When printing out the
per-file data, I think it would be useful to track and print the block
number where the highest LSN in that file was found. I have
definitely had cases where I suspected, but was not certain of,
corruption. One could use a tool like this to hunt for problems, and
then use something like pg_filedump to dump the offending blocks.
That would be a lot easier than running pg_filedump on the whole file
and grepping through the output.

Corrected.

Similarly, I see no reason for the restriction imposed by
check_path_belongs_to_pgdata(). I've had people mail me one data
file; why shouldn't I be able to run this tool on it? It's a
read-only utility.

- if (0 != FindMaxLSNinDir(pathbuf, maxlsn, false)) and similar is not
PostgreSQL style.

+ printf(_("%s compute the maximum LSN in PostgreSQL data
pages.\n\n"), progname);

Fixed.

+       /*
+        * Don't allow pg_computemaxlsn 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

This utility only reads files; it does not modify them. So this seems
unnecessary. I assume it's blindly copied from somewhere else.

+ fprintf(stderr, _("%s: \"%s\" not a valid data

directory.\n"),

Extra space.

+       /*
+        * 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);

Again, this might be appropriate for pg_resetxlog, but I see no reason
for the restriction here. The output might be inaccurate in that
case, but if you're using this tool you're required to know what
you're doing.

Fixed.

+    For safety reasons, you must specify the data directory on the
command line.
+    <command>pg_computemaxlsn</command> does not use the environment

variable

+ <envar>PGDATA</>.

Same thing here. I think using PGDATA would be quite appropriate for
this utility.

Fixed.

+   <para>
+    This command must not be used when the server is
+    running.  <command>pg_computemaxlsn</command> will refuse to start up

if

+    it finds a server lock file in the data directory.  If the
+    server crashed then a lock file might have been left
+    behind; in that case you can remove the lock file to allow
+    <command>pg_computemaxlsn</command> to run.  But before you do
+    so, make doubly certain that there is no server process still alive.
+   </para>

More of the same paranoia.

Overall my feeling is that this can be simplified quite a lot.
There's a lot of stuff in here that's really not needed.

Corrected.

Please find the updated patch attached.

Regards,
Hari babu.

Attachments:

pg_computemaxlsn_v7.patchapplication/octet-stream; name=pg_computemaxlsn_v7.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,412 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ static int	quiet = false;
+ 
+ static int	FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static int	FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn);
+ static int	FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ 
+ /*
+  *	This function validates the given cluster directory - we search for a
+  *	small set of subdirectories that we expect to find in a valid data directory.
+  *	directory.	If any of the subdirectories are missing (or secured against
+  *	us) we display an error message and exit()
+  *
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0
+ 			|| (!S_ISDIR(statBuf.st_mode)))
+ 			return false;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (name[pos] == '_')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 			pos++;
+ 	}
+ 	if (name[pos] == '.')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 			pos++;
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"quiet", required_argument, NULL, 'q'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 	int			optindex;
+ 	int			c;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	int			result = 0;
+ 	struct stat statbuf;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "q", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'q':
+ 				quiet = true;
+ 				break;
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	LsnSearchPath = argv[optind];
+ 
+ 	if (LsnSearchPath == NULL)
+ 		LsnSearchPath = getenv("PGDATA");
+ 
+ 	if (LsnSearchPath == NULL)
+ 	{
+ 		fprintf(stderr, _("%s: no file/directory specified\n"), progname);
+ 		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 		exit(1);
+ 	}
+ 
+ 	/* By default we need to compute max lsn for database */
+ 	if (check_data_dir(LsnSearchPath))
+ 	{
+ 		result = FindMaxLSNinPgData(LsnSearchPath, &maxLSN);
+ 	}
+ 	else
+ 	{
+ 		if (lstat(LsnSearchPath, &statbuf) < 0)
+ 		{
+ 			if (errno == ENOENT)
+ 			{
+ 				fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 						progname, LsnSearchPath);
+ 			}
+ 			else
+ 			{
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, LsnSearchPath, strerror(errno));
+ 			}
+ 			exit(1);
+ 		}
+ 
+ 		if (S_ISREG(statbuf.st_mode))
+ 			result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 		else
+ 			result = FindMaxLSNinDir(LsnSearchPath, &maxLSN);
+ 	}
+ 
+ 	if (0 != result)
+ 	{
+ 		/* Message already provided, simply exit */
+ 		exit(1);
+ 	}
+ 
+ 	XLByteToSeg(maxLSN, logSegNo);
+ 
+ 	printf("Maximum LSN found is: %X/%X \nWAL segment file name (fileid,seg): %X/%X\n",
+ 		   (uint32) (maxLSN >> 32), (uint32) maxLSN,
+ 		   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  *
+  */
+ static int
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 	XLogRecPtr	pagemaxlsn = 0;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: .. file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return -1;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and validate; if we find invalid page log the
+ 	 * details of page and continue to next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to next page  \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));
+ 			return -1;
+ 		}
+ 
+ 		pagelsn = PageGetLSN(buffer);
+ 		if (pagemaxlsn < pagelsn)
+ 			pagemaxlsn = pagelsn;
+ 
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 
+ 	if (*maxlsn < pagemaxlsn)
+ 		*maxlsn = pagemaxlsn;
+ 
+ 	if (!quiet)
+ 	{
+ 		XLogSegNo	logSegNo = 0;
+ 
+ 		XLByteToSeg(pagemaxlsn, logSegNo);
+ 		printf("File:%s Maximum LSN found is: %X/%X \nWAL segment file name (fileid,seg): %X/%X\n",
+ 			   filename, (uint32) (pagemaxlsn >> 32), (uint32) pagemaxlsn,
+ 			   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 	}
+ 
+ 	return 0;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static int
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return -1;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all the local/global temporary files, and read and read all
+ 		 * remaining relfinenode files
+ 		 */
+ 		if (!validateRelfilenodename(de->d_name))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		if (S_ISREG(statbuf.st_mode))
+ 			(void) FindMaxLSNinFile(pathbuf, maxlsn);
+ 		else
+ 			(void) FindMaxLSNinDir(pathbuf, maxlsn);
+ 	}
+ 
+ 	closedir(dir);
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static int
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (FindMaxLSNinDir(pathbuf, maxlsn))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (FindMaxLSNinDir(pathbuf, maxlsn))
+ 		return -1;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	return FindMaxLSNinDir(pathbuf, maxlsn);
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s FILE/DIRECTORY\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -V, --version        output version information, then exit\n"));
+ 	printf(_("  -?, --help           show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
***************
*** 208,212 **** pages.
--- 208,213 ----
   &pgtesttiming;
   &pgupgrade;
   &pgxlogdump;
+  &pgcomputemaxlsn;
   </sect1>
  </appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 124,129 ****
--- 124,130 ----
  <!ENTITY pgbench         SYSTEM "pgbench.sgml">
  <!ENTITY pgarchivecleanup SYSTEM "pgarchivecleanup.sgml">
  <!ENTITY pgbuffercache   SYSTEM "pgbuffercache.sgml">
+ <!ENTITY pgcomputemaxlsn   SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgcrypto        SYSTEM "pgcrypto.sgml">
  <!ENTITY pgfreespacemap  SYSTEM "pgfreespacemap.sgml">
  <!ENTITY pgrowlocks      SYSTEM "pgrowlocks.sgml">
*** /dev/null
--- b/doc/src/sgml/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,51 ----
+ <!--
+ doc/src/sgml/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="pgcomputemaxlsn">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><replaceable>file or directory</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages
+    in the specified file or directory.
+   </para>
+ 
+   <para>
+    This utility can only be run by the user who installed the server, because
+    it requires read/write access to the data directory.
+    If user doesn't provide the file or directory to find the max lsn then
+    <command>pg_computemaxlsn</command> use the environment variable <envar>PGDATA</>.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options print
+    the <application>pg_computemaxlsn</application> version and exit.  The
+    options <option>-?</> and <option>--help</> show supported syntax,
+    and exit.
+   </para>
+ 
+  </refsect1>
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 139,144 **** PostgreSQL documentation
--- 139,153 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       by specifing file or folder or data directory. Once larger WAL segment file is found use <option>-l</> option
+       for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 43,49 **** my @contrib_uselibpgcommon = (
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 43,49 ----
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo', 	 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#24Robert Haas
robertmhaas@gmail.com
In reply to: Amit kapila (#1)

On Thu, Jul 4, 2013 at 2:14 AM, Amit Kapila <amit.kapila@huawei.com> wrote:

It can cause error "too many levels of symbolic links"

Sure, so you report the error and exit. No problem.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Robert Haas
robertmhaas@gmail.com
In reply to: Amit kapila (#1)

This looks better.

+ 		fprintf(stderr, _("%s: .. file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));

Weird error message style - what's with the ".."?

+ 		fprintf(stderr, _("%s: .. file \"%s\" length is more than segment
size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);

Again.

+ 			fprintf(stderr, _("%s: could not seek to next page  \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));

I think this should be written as: could not seek to offset NUMBER in
file "PATH"

+ 			fprintf(stderr, _("%s: could not read file \"%s\": %s\n"),
+ 					progname, filename, strerror(errno));

And this one as: could not read file "PATH" at offset NUMBER

+ printf("File:%s Maximum LSN found is: %X/%X \nWAL segment file
name (fileid,seg): %X/%X\n",

I think that we don't need to display the WAL segment file name for
the per-file progress updates. Instead, let's show the block number
where that LSN was found, like this:

Highest LSN for file "%s" is %X/%X in block %u.

The overall output can stay as you have it.

+ if (0 != result)

Coding style.

+ static int
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)

It seems that this function, and a few others, use -1 for a failure
return, 0 for success, and all others undefined. Although this is a
defensible choice, I think it would be more PG-like to make the return
value bool and use true for success and false for failure.

+ 		if (S_ISREG(statbuf.st_mode))
+ 			(void) FindMaxLSNinFile(pathbuf, maxlsn);
+ 		else
+ 			(void) FindMaxLSNinDir(pathbuf, maxlsn);

I don't see why we're throwing away the return value here. I would
expect the function to have a "bool result = true" at the top and sent
result = false if one of these functions returns false. At the end,
it returns result.

+    This utility can only be run by the user who installed the server, because
+    it requires read/write access to the data directory.

False.

+ 	LsnSearchPath = argv[optind];
+
+ 	if (LsnSearchPath == NULL)
+ 		LsnSearchPath = getenv("PGDATA");

I think you should write the code so that the tool loops over its
input arguments; if none, it processes $PGDATA. (Don't forget to
update the syntax synopsis and documentation to match.)

I think the documentation should say something about the intended uses
of this tool, including cautions against using it for things to which
it is not well-suited. I guess the threshold question for this patch
is whether those uses are enough to justify including the tool in the
core distribution.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26Hari Babu
haribabu.kommi@huawei.com
In reply to: Robert Haas (#25)
1 attachment(s)

On Thursday, July 04, 2013 11:19 PM Robert Haas wrote:

+ 		fprintf(stderr, _("%s: .. file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));

Weird error message style - what's with the ".."?

+ fprintf(stderr, _("%s: .. file \"%s\" length is more than

segment

size: %d.\n"),
+ progname, filename, RELSEG_SIZE);

Again.

Corrected.

+ fprintf(stderr, _("%s: could not seek to next page

\"%s\": %s\n"),

+ progname, filename,

strerror(errno));

I think this should be written as: could not seek to offset NUMBER in
file "PATH"

+ fprintf(stderr, _("%s: could not read file \"%s\":

%s\n"),

+ progname, filename,

strerror(errno));

And this one as: could not read file "PATH" at offset NUMBER

+ printf("File:%s Maximum LSN found is: %X/%X \nWAL segment

file

name (fileid,seg): %X/%X\n",

I think that we don't need to display the WAL segment file name for
the per-file progress updates. Instead, let's show the block number
where that LSN was found, like this:

Highest LSN for file "%s" is %X/%X in block %u.

The overall output can stay as you have it.

Changed as per your suggestion.

+ if (0 != result)

Coding style.

+ static int
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)

It seems that this function, and a few others, use -1 for a failure
return, 0 for success, and all others undefined. Although this is a
defensible choice, I think it would be more PG-like to make the return
value bool and use true for success and false for failure.

+ 		if (S_ISREG(statbuf.st_mode))
+ 			(void) FindMaxLSNinFile(pathbuf, maxlsn);
+ 		else
+ 			(void) FindMaxLSNinDir(pathbuf, maxlsn);

I don't see why we're throwing away the return value here. I would
expect the function to have a "bool result = true" at the top and sent
result = false if one of these functions returns false. At the end,
it returns result.

Fixed.

+ This utility can only be run by the user who installed the server,

because

+ it requires read/write access to the data directory.

False.

Removed.

+ 	LsnSearchPath = argv[optind];
+
+ 	if (LsnSearchPath == NULL)
+ 		LsnSearchPath = getenv("PGDATA");

I think you should write the code so that the tool loops over its
input arguments; if none, it processes $PGDATA. (Don't forget to
update the syntax synopsis and documentation to match.)

Added the functionality of multiple file or directories parsing and printing
Max LSN for each input argument.

I think the documentation should say something about the intended uses
of this tool, including cautions against using it for things to which
it is not well-suited. I guess the threshold question for this patch
is whether those uses are enough to justify including the tool in the
core distribution.

Added some use cases and notes regarding the tool. Please suggest if any
More information needs to be documented.

Thanks for the review, please find the updated patch attached in the mail.

Regards,
Hari babu.

Attachments:

pg_computemaxlsn_v8.patchapplication/octet-stream; name=pg_computemaxlsn_v8.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,420 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ static int	quiet = false;
+ 
+ static bool FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ 
+ /*
+  * This function validates the given cluster directory - we search for a
+  * small set of subdirectories that we expect to find in a valid data directory.
+  * directory. If any of the subdirectories are missing, then it treats as a
+  * normal directory.
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0
+ 			|| (!S_ISDIR(statBuf.st_mode)))
+ 			return false;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (name[pos] == '_')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 			pos++;
+ 	}
+ 	if (name[pos] == '.')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 			pos++;
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"quiet", no_argument, NULL, 'q'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 	int			optindex;
+ 	int			c;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	bool		result = true;
+ 	struct stat statbuf;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "q", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'q':
+ 				quiet = true;
+ 				break;
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* Loop over all input arguements */
+ 	do
+ 	{
+ 		if (optind < argc)
+ 			LsnSearchPath = argv[optind];
+ 		else
+ 			LsnSearchPath = getenv("PGDATA");
+ 
+ 		if (LsnSearchPath == NULL)
+ 		{
+ 			fprintf(stderr, _("%s: no file/directory specified\n"), progname);
+ 			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 			exit(1);
+ 		}
+ 
+ 		maxLSN = 0;
+ 
+ 		/* Check for the provided search path is a data directory? */
+ 		if (check_data_dir(LsnSearchPath))
+ 		{
+ 			result = FindMaxLSNinPgData(LsnSearchPath, &maxLSN);
+ 		}
+ 		else
+ 		{
+ 			if (lstat(LsnSearchPath, &statbuf) < 0)
+ 			{
+ 				if (errno == ENOENT)
+ 				{
+ 					fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 							progname, LsnSearchPath);
+ 				}
+ 				else
+ 				{
+ 					fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 							progname, LsnSearchPath, strerror(errno));
+ 				}
+ 				exit(1);
+ 			}
+ 
+ 			if (S_ISREG(statbuf.st_mode))
+ 				result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 			else
+ 				result = FindMaxLSNinDir(LsnSearchPath, &maxLSN);
+ 		}
+ 
+ 		if (!result)
+ 		{
+ 			/* Message already provided, simply exit */
+ 			exit(1);
+ 		}
+ 
+ 		XLByteToSeg(maxLSN, logSegNo);
+ 
+ 		printf("Maximum LSN found in \"%s\" is: %X/%X \n"
+ 			   "WAL segment file name (fileid,seg): %X/%X\n",
+ 			   LsnSearchPath, (uint32) (maxLSN >> 32), (uint32) maxLSN,
+ 			   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 	} while (++optind < argc);
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  */
+ static bool
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 	XLogRecPtr	filemaxlsn = 0;
+ 	uint32		filemaxlsn_block = 0;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and find the LSN of the page and continue to
+ 	 * next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to offset %u in file \"%s\": %s\n"),
+ 					progname, blocknum, filename, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\" at offset %u: %s\n"),
+ 					progname, filename, blocknum, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		/*
+ 		 * Remember this lsn as the highest (if it is)
+ 		 */
+ 		pagelsn = PageGetLSN(buffer);
+ 		if (filemaxlsn < pagelsn)
+ 		{
+ 			filemaxlsn = pagelsn;
+ 			filemaxlsn_block = blocknum;
+ 		}
+ 
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 
+ 	if (*maxlsn < filemaxlsn)
+ 		*maxlsn = filemaxlsn;
+ 
+ 	if (!quiet)
+ 		printf("Highest LSN for file:\"%s\" is: %X/%X in block %u\n",
+ 			   filename, (uint32) (filemaxlsn >> 32), (uint32) filemaxlsn,
+ 			   filemaxlsn_block);
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static bool
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 	bool		result = true;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all invalid files, and read remaining relfilenode files
+ 		 */
+ 		if (!validateRelfilenodename(de->d_name))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		if (S_ISREG(statbuf.st_mode))
+ 			result = FindMaxLSNinFile(pathbuf, maxlsn);
+ 		else
+ 			result = FindMaxLSNinDir(pathbuf, maxlsn);
+ 	}
+ 
+ 	closedir(dir);
+ 	return result;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static bool
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		return false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		return false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	return FindMaxLSNinDir(pathbuf, maxlsn);
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s [OPTION] [FILE/DIRECTORY...]\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -q, --quiet        stops the output of highest LSN in a file\n"));
+ 	printf(_("  -V, --version      output version information, then exit\n"));
+ 	printf(_("  -?, --help         show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
***************
*** 208,212 **** pages.
--- 208,213 ----
   &pgtesttiming;
   &pgupgrade;
   &pgxlogdump;
+  &pgcomputemaxlsn;
   </sect1>
  </appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 124,129 ****
--- 124,130 ----
  <!ENTITY pgbench         SYSTEM "pgbench.sgml">
  <!ENTITY pgarchivecleanup SYSTEM "pgarchivecleanup.sgml">
  <!ENTITY pgbuffercache   SYSTEM "pgbuffercache.sgml">
+ <!ENTITY pgcomputemaxlsn   SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgcrypto        SYSTEM "pgcrypto.sgml">
  <!ENTITY pgfreespacemap  SYSTEM "pgfreespacemap.sgml">
  <!ENTITY pgrowlocks      SYSTEM "pgrowlocks.sgml">
*** /dev/null
--- b/doc/src/sgml/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,86 ----
+ <!--
+ doc/src/sgml/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="pgcomputemaxlsn">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><replaceable>option</replaceable></arg>
+    <arg rep="repeat"><replaceable>file or directory</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages
+    in the specified list of files or directories.
+   </para>
+ 
+   <para>
+    If user doesn't provide the file or directory to find the max lsn then
+    <command>pg_computemaxlsn</command> use the environment variable <envar>PGDATA</>
+    if exists otherwise reports an error.
+   </para>
+ 
+   <para>
+    The <option>-q</> and <option>--quiet</> options stops the print of
+    highest LSN in a file.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options
+    print the <application>pg_computemaxlsn</application> version and exit. The
+    options <option>-?</> and <option>--help</> show supported syntax, and exit.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>use cases</title>
+   <para>
+    This utility can be used to avoid more data corruption by finding out the
+    maximum lsn in the data directory. which is required by the <command>pg_resetxlog</command>
+    to provide the new WAL segment file name.
+   </para>
+ 
+   <para>
+    This utility can also be used to decide whether backup is required or not when the data page
+    in old-master precedes the last applied LSN in old-standby (i.e., new-master) at the
+    moment of the failover.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+   <screen>
+ pg_computemaxlsn> ./pg_computemaxlsn -q ../../../data/
+ Maximum LSN found in "../../../data/" is: 0/181B090
+ WAL segment file name (fileid,seg): 0/1
+   </screen>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+   <para>
+    This utility just parse the header of the page and provides the LSN, please be sure
+    when using this tool on directory which is not a data directory.
+   </para>
+  </refsect1>
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 139,144 **** PostgreSQL documentation
--- 139,153 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       by specifing file or folder or data directory. Once larger WAL segment file is found use <option>-l</> option
+       for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 43,49 **** my @contrib_uselibpgcommon = (
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 43,49 ----
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo', 	 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#27Hari Babu
haribabu.kommi@huawei.com
In reply to: Amit kapila (#1)
1 attachment(s)

On Friday, July 05, 2013 6:48 PM Hari Babu wrote:

On Thursday, July 04, 2013 11:19 PM Robert Haas wrote:

The patch is updated with the following changes.

1.If the input data is data directory, all the errors occurred are displayed
at once instead of one error at a time.
2.Fixed the problem of replacing result of the previous file or directory
result with new one.
3.Update the docs.

Thanks for the review, please find the updated patch attached in the mail.

Regards,
Hari babu.

Attachments:

pg_computemaxlsn_v9.patchapplication/octet-stream; name=pg_computemaxlsn_v9.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,430 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ static int	quiet = false;
+ 
+ static bool FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ 
+ /*
+  * This function validates the given cluster directory - we search for a
+  * small set of subdirectories that we expect to find in a valid data directory.
+  * directory. If any of the subdirectories are missing, then it treats as a
+  * normal directory.
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0
+ 			|| (!S_ISDIR(statBuf.st_mode)))
+ 			return false;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (name[pos] == '_')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 			pos++;
+ 	}
+ 	if (name[pos] == '.')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 			pos++;
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"quiet", no_argument, NULL, 'q'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 	int			optindex;
+ 	int			c;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	bool		result = true;
+ 	struct stat statbuf;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "q", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'q':
+ 				quiet = true;
+ 				break;
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* Loop over all input arguements */
+ 	do
+ 	{
+ 		if (optind < argc)
+ 			LsnSearchPath = argv[optind];
+ 		else
+ 			LsnSearchPath = getenv("PGDATA");
+ 
+ 		if (LsnSearchPath == NULL)
+ 		{
+ 			fprintf(stderr, _("%s: no file/directory specified\n"), progname);
+ 			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 			exit(1);
+ 		}
+ 
+ 		maxLSN = 0;
+ 
+ 		/* Check for the provided search path is a data directory? */
+ 		if (check_data_dir(LsnSearchPath))
+ 		{
+ 			result = FindMaxLSNinPgData(LsnSearchPath, &maxLSN);
+ 		}
+ 		else
+ 		{
+ 			if (lstat(LsnSearchPath, &statbuf) < 0)
+ 			{
+ 				if (errno == ENOENT)
+ 				{
+ 					fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 							progname, LsnSearchPath);
+ 				}
+ 				else
+ 				{
+ 					fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 							progname, LsnSearchPath, strerror(errno));
+ 				}
+ 				exit(1);
+ 			}
+ 
+ 			if (S_ISREG(statbuf.st_mode))
+ 				result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 			else
+ 				result = FindMaxLSNinDir(LsnSearchPath, &maxLSN);
+ 		}
+ 
+ 		if (!result)
+ 		{
+ 			/* Message already provided, simply exit */
+ 			exit(1);
+ 		}
+ 
+ 		XLByteToSeg(maxLSN, logSegNo);
+ 
+ 		printf("Maximum LSN found in \"%s\" is: %X/%X \n"
+ 			   "WAL segment file name (fileid,seg): %X/%X\n",
+ 			   LsnSearchPath, (uint32) (maxLSN >> 32), (uint32) maxLSN,
+ 			   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 	} while (++optind < argc);
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  */
+ static bool
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 	XLogRecPtr	filemaxlsn = 0;
+ 	uint32		filemaxlsn_block = 0;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and find the LSN of the page and continue to
+ 	 * next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to offset %u in file \"%s\": %s\n"),
+ 					progname, blocknum, filename, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\" at offset %u: %s\n"),
+ 					progname, filename, blocknum, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		/*
+ 		 * Remember this lsn as the highest (if it is)
+ 		 */
+ 		pagelsn = PageGetLSN(buffer);
+ 		if (filemaxlsn < pagelsn)
+ 		{
+ 			filemaxlsn = pagelsn;
+ 			filemaxlsn_block = blocknum;
+ 		}
+ 
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 
+ 	if (*maxlsn < filemaxlsn)
+ 		*maxlsn = filemaxlsn;
+ 
+ 	if (!quiet)
+ 		printf("Highest LSN for file:\"%s\" is: %X/%X in block %u\n",
+ 			   filename, (uint32) (filemaxlsn >> 32), (uint32) filemaxlsn,
+ 			   filemaxlsn_block);
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static bool
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 	bool		result = true;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all invalid files, and read remaining relfilenode files
+ 		 */
+ 		if (!validateRelfilenodename(de->d_name))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		if (S_ISREG(statbuf.st_mode))
+ 		{
+ 			if (!FindMaxLSNinFile(pathbuf, maxlsn))
+ 				result = false;
+ 		}
+ 		else
+ 		{
+ 			if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 				result = false;
+ 		}
+ 	}
+ 
+ 	closedir(dir);
+ 	return result;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static bool
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 	bool		result = true;
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	return result;
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s [OPTION] [FILE/DIRECTORY...]\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -q, --quiet        stops the output of highest LSN in a file\n"));
+ 	printf(_("  -V, --version      output version information, then exit\n"));
+ 	printf(_("  -?, --help         show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
***************
*** 208,212 **** pages.
--- 208,213 ----
   &pgtesttiming;
   &pgupgrade;
   &pgxlogdump;
+  &pgcomputemaxlsn;
   </sect1>
  </appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 124,129 ****
--- 124,130 ----
  <!ENTITY pgbench         SYSTEM "pgbench.sgml">
  <!ENTITY pgarchivecleanup SYSTEM "pgarchivecleanup.sgml">
  <!ENTITY pgbuffercache   SYSTEM "pgbuffercache.sgml">
+ <!ENTITY pgcomputemaxlsn   SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgcrypto        SYSTEM "pgcrypto.sgml">
  <!ENTITY pgfreespacemap  SYSTEM "pgfreespacemap.sgml">
  <!ENTITY pgrowlocks      SYSTEM "pgrowlocks.sgml">
*** /dev/null
--- b/doc/src/sgml/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,100 ----
+ <!--
+ doc/src/sgml/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="pgcomputemaxlsn">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><replaceable>option</replaceable></arg>
+    <arg rep="repeat"><replaceable>file or directory</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages
+    in the specified list of files or directories.
+   </para>
+ 
+   <para>
+    If user doesn't provide the file or directory to find the max lsn then
+    <command>pg_computemaxlsn</command> use the environment variable <envar>PGDATA</>
+    if exists otherwise reports an error.
+   </para>
+ 
+   <para>
+    The <option>-q</> and <option>--quiet</> options stops the print of
+    highest LSN in a file.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options
+    print the <application>pg_computemaxlsn</application> version and exit. The
+    options <option>-?</> and <option>--help</> show supported syntax, and exit.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>use cases</title>
+   <para>
+    This utility can be used to avoid more data corruption by finding out the
+    maximum lsn in the data directory. which is required by the <command>pg_resetxlog</command>
+    to provide the new WAL segment file name.
+   </para>
+ 
+   <para>
+    This utility can also be used to decide whether backup is required or not when the data page
+    in old-master precedes the last applied LSN in old-standby (i.e., new-master) at the
+    moment of the failover.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+   <screen>
+ pg_computemaxlsn> ./pg_computemaxlsn -q ../../../data/
+ Maximum LSN found in "../../../data/" is: 0/181B090
+ WAL segment file name (fileid,seg): 0/1
+   </screen>
+ 
+   <screen>
+ pg_computemaxlsn> ./pg_computemaxlsn -q ../../../data/base/126*
+ Maximum LSN found in "../../../data/base/12625" is: 0/17D5360
+ WAL segment file name (fileid,seg): 0/1
+ Maximum LSN found in "../../../data/base/12630" is: 0/181B090
+ WAL segment file name (fileid,seg): 0/1
+   </screen>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+   <para>
+    This utility just parse the header of the page and provides the LSN, please be sure
+    when using this tool on directory which is not a data directory.
+   </para>
+ 
+   <para>
+    If any error occurs during the finding of max LSN in the provided file or directory,
+    the max lsn is not printed. Once the reported problem solves need to run the tool again
+    for proper result.
+   </para>
+  </refsect1>
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 139,144 **** PostgreSQL documentation
--- 139,153 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       by specifing file or folder or data directory. Once larger WAL segment file is found use <option>-l</> option
+       for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 43,49 **** my @contrib_uselibpgcommon = (
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 43,49 ----
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo', 	 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#28'Andres Freund'
andres@2ndquadrant.com
In reply to: Hari Babu (#27)

On 2013-07-08 16:17:54 +0530, Hari Babu wrote:

+    This utility can also be used to decide whether backup is required or not when the data page
+    in old-master precedes the last applied LSN in old-standby (i.e., new-master) at the
+    moment of the failover.
+   </para>
+  </refsect1>

I don't think this is safe in any interesting set of cases. Am I missing
something?
People falsely thinking that it can be used for this is the primary
reason for me objecting the patch...

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Amit Kapila
amit.kapila@huawei.com
In reply to: 'Andres Freund' (#28)

On Monday, July 08, 2013 4:26 PM Andres Freund wrote:

On 2013-07-08 16:17:54 +0530, Hari Babu wrote:

+ This utility can also be used to decide whether backup is

required or not when the data page

+ in old-master precedes the last applied LSN in old-standby

(i.e., new-master) at the

+    moment of the failover.
+   </para>
+  </refsect1>

I don't think this is safe in any interesting set of cases. Am I
missing
something?

No, you are not missing anything. It can be only used to find max LSN in
database which can avoid further corruption

People falsely thinking that it can be used for this is the primary
reason for me objecting the patch...

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30'Andres Freund'
andres@2ndquadrant.com
In reply to: Amit Kapila (#29)

On 2013-07-08 17:10:43 +0530, Amit Kapila wrote:

On Monday, July 08, 2013 4:26 PM Andres Freund wrote:

On 2013-07-08 16:17:54 +0530, Hari Babu wrote:

+ This utility can also be used to decide whether backup is

required or not when the data page

+ in old-master precedes the last applied LSN in old-standby

(i.e., new-master) at the

+    moment of the failover.
+   </para>
+  </refsect1>

I don't think this is safe in any interesting set of cases. Am I
missing
something?

No, you are not missing anything. It can be only used to find max LSN in
database which can avoid further corruption

Why is the patch submitted documenting it as a use-case then? I find it
rather scary if the *patch authors* document a known unsafe use case as
one of the known use-cases.

Greetings,

Andres Freund

--
Andres Freund http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31Hari Babu
haribabu.kommi@huawei.com
In reply to: 'Andres Freund' (#30)
1 attachment(s)

On Monday, July 08, 2013 5:16 PM Andres Freund wrote:

On 2013-07-08 17:10:43 +0530, Amit Kapila wrote:

On Monday, July 08, 2013 4:26 PM Andres Freund wrote:

On 2013-07-08 16:17:54 +0530, Hari Babu wrote:

+ This utility can also be used to decide whether backup is

required or not when the data page

+ in old-master precedes the last applied LSN in old-standby

(i.e., new-master) at the

+    moment of the failover.
+   </para>
+  </refsect1>

I don't think this is safe in any interesting set of cases. Am I
missing
something?

No, you are not missing anything. It can be only used to find max LSN in
database which can avoid further corruption

Why is the patch submitted documenting it as a use-case then? I find it
rather scary if the *patch authors* document a known unsafe use case as
one of the known use-cases.

I got the problem which can occur with the specified use case. Removed the
wrong use case specified above.
Thanks for the review, please find the updated patch attached in the mail.

Regards,
Hari babu.

Attachments:

pg_computemaxlsn_v10.patchapplication/octet-stream; name=pg_computemaxlsn_v10.patchDownload
*** a/contrib/Makefile
--- b/contrib/Makefile
***************
*** 31,36 **** SUBDIRS = \
--- 31,37 ----
  		passwordcheck	\
  		pg_archivecleanup \
  		pg_buffercache	\
+ 		pg_computemaxlsn \
  		pg_freespacemap \
  		pg_standby	\
  		pg_stat_statements \
*** /dev/null
--- b/contrib/pg_computemaxlsn/Makefile
***************
*** 0 ****
--- 1,22 ----
+ # contrib/pg_computemaxlsn/Makefile
+ 
+ PGFILEDESC = "pg_computemaxlsn - an utility to find max LSN from data pages"
+ PGAPPICON = win32
+ 
+ PROGRAM  = pg_computemaxlsn
+ OBJS = pg_computemaxlsn.o $(WIN32RES)
+ 
+ PG_CPPFLAGS  = -I$(srcdir)
+ PG_LIBS = $(libpq_pgport)
+ 
+ 
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/pg_computemaxlsn
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/pg_computemaxlsn/pg_computemaxlsn.c
***************
*** 0 ****
--- 1,430 ----
+ /*-------------------------------------------------------------------------
+  *
+  * pg_computemaxlsn.c
+  *	  A utility to compute the maximum LSN in data pages
+  *
+  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * IDENTIFICATION
+  * contrib/pg_computemaxlsn/pg_computemaxlsn.c
+  *
+  *-------------------------------------------------------------------------
+  */
+ 
+ /*
+  * We have to use postgres.h not postgres_fe.h here, because there's so much
+  * backend-only stuff for reading data files we need.  But we need a
+  * frontend-ish environment otherwise.	Hence this ugly hack.
+  */
+ #define FRONTEND 1
+ 
+ #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>
+ 
+ #include "getopt_long.h"
+ 
+ #include "access/xlog_internal.h"
+ #include "catalog/catalog.h"
+ #include "storage/bufpage.h"
+ #include "storage/fd.h"
+ 
+ /* Page header size */
+ #define PAGEHDRSZ (sizeof(PageHeaderData))
+ 
+ extern int	optind;
+ extern char *optarg;
+ static const char *progname;
+ static int	quiet = false;
+ 
+ static bool FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn);
+ static bool FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn);
+ static void usage(void);
+ 
+ /*
+  * This function validates the given cluster directory - we search for a
+  * small set of subdirectories that we expect to find in a valid data directory.
+  * directory. If any of the subdirectories are missing, then it treats as a
+  * normal directory.
+  */
+ static bool
+ check_data_dir(const char *pg_data)
+ {
+ 	char		subDirName[MAXPGPATH];
+ 	int			dnum;
+ 
+ 	/* start check with top-most directory */
+ 	const char *requiredSubdirs[] = {"", "base", "global", "pg_tblspc",
+ 		"pg_multixact", "pg_subtrans", "pg_clog", "pg_twophase",
+ 	"pg_xlog"};
+ 
+ 	for (dnum = 0; dnum < lengthof(requiredSubdirs); ++dnum)
+ 	{
+ 		struct stat statBuf;
+ 
+ 		snprintf(subDirName, sizeof(subDirName), "%s%s%s", pg_data,
+ 		/* Win32 can't stat() a directory with a trailing slash. */
+ 				 *requiredSubdirs[dnum] ? "/" : "",
+ 				 requiredSubdirs[dnum]);
+ 
+ 		if (stat(subDirName, &statBuf) != 0
+ 			|| (!S_ISDIR(statBuf.st_mode)))
+ 			return false;
+ 	}
+ 
+ 	return true;
+ }
+ 
+ /*
+  * relfilenode name validation.
+  * Format with_ext == true	[0-9]+[ \w | _vm | _fsm | _init ][\.][0-9]*
+  *		  with_ext == false [0-9]+
+  */
+ static bool
+ validateRelfilenodename(char *name)
+ {
+ 	int			pos = 0;
+ 
+ 	while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 		pos++;
+ 
+ 	if (name[pos] == '_')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= 'a') && (name[pos] <= 'z'))
+ 			pos++;
+ 	}
+ 	if (name[pos] == '.')
+ 	{
+ 		pos++;
+ 		while ((name[pos] >= '0') && (name[pos] <= '9'))
+ 			pos++;
+ 	}
+ 
+ 	if (name[pos] == 0)
+ 		return true;
+ 
+ 	return false;
+ }
+ 
+ int
+ main(int argc, char *argv[])
+ {
+ 	static struct option long_options[] = {
+ 		{"quiet", no_argument, NULL, 'q'},
+ 		{NULL, 0, NULL, 0}
+ 	};
+ 	int			optindex;
+ 	int			c;
+ 	char	   *LsnSearchPath = NULL;
+ 	XLogRecPtr	maxLSN = 0;
+ 	XLogSegNo	logSegNo = 0;
+ 	bool		result = true;
+ 	struct stat statbuf;
+ 
+ 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_computemaxlsn"));
+ 
+ 	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_computemaxlsn (PostgreSQL) " PG_VERSION);
+ 			exit(0);
+ 		}
+ 	}
+ 
+ 	while ((c = getopt_long(argc, argv, "q", long_options, &optindex)) != -1)
+ 	{
+ 		switch (c)
+ 		{
+ 			case 'q':
+ 				quiet = true;
+ 				break;
+ 			default:
+ 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* Loop over all input arguements */
+ 	do
+ 	{
+ 		if (optind < argc)
+ 			LsnSearchPath = argv[optind];
+ 		else
+ 			LsnSearchPath = getenv("PGDATA");
+ 
+ 		if (LsnSearchPath == NULL)
+ 		{
+ 			fprintf(stderr, _("%s: no file/directory specified\n"), progname);
+ 			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ 			exit(1);
+ 		}
+ 
+ 		maxLSN = 0;
+ 
+ 		/* Check for the provided search path is a data directory? */
+ 		if (check_data_dir(LsnSearchPath))
+ 		{
+ 			result = FindMaxLSNinPgData(LsnSearchPath, &maxLSN);
+ 		}
+ 		else
+ 		{
+ 			if (lstat(LsnSearchPath, &statbuf) < 0)
+ 			{
+ 				if (errno == ENOENT)
+ 				{
+ 					fprintf(stderr, _("%s: file or directory \"%s\" does not exists.\n"),
+ 							progname, LsnSearchPath);
+ 				}
+ 				else
+ 				{
+ 					fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 							progname, LsnSearchPath, strerror(errno));
+ 				}
+ 				exit(1);
+ 			}
+ 
+ 			if (S_ISREG(statbuf.st_mode))
+ 				result = FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+ 			else
+ 				result = FindMaxLSNinDir(LsnSearchPath, &maxLSN);
+ 		}
+ 
+ 		if (!result)
+ 		{
+ 			/* Message already provided, simply exit */
+ 			exit(1);
+ 		}
+ 
+ 		XLByteToSeg(maxLSN, logSegNo);
+ 
+ 		printf("Maximum LSN found in \"%s\" is: %X/%X \n"
+ 			   "WAL segment file name (fileid,seg): %X/%X\n",
+ 			   LsnSearchPath, (uint32) (maxLSN >> 32), (uint32) maxLSN,
+ 			   (uint32) (logSegNo >> 32), (uint32) (logSegNo));
+ 	} while (++optind < argc);
+ 
+ 	return 0;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the one of data file (relnode file).
+  */
+ static bool
+ FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn)
+ {
+ 	XLogRecPtr	pagelsn;
+ 	off_t		len,
+ 				seekpos;
+ 	uint32		nblocks,
+ 				blocknum;
+ 	char		buffer[PAGEHDRSZ];
+ 	int			nbytes;
+ 	int			fd;
+ 	XLogRecPtr	filemaxlsn = 0;
+ 	uint32		filemaxlsn_block = 0;
+ 
+ 	if ((fd = open(filename, O_RDONLY | PG_BINARY, 0)) < 0)
+ 	{
+ 		/*
+ 		 * If file does not exist or we can't read it. give error
+ 		 */
+ 		fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	/* Calculate the number of pages in file */
+ 	len = lseek(fd, 0L, SEEK_END);
+ 	if (len < 0)
+ 	{
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" for seeking: %s\n"),
+ 				progname, filename, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	nblocks = (len / BLCKSZ);
+ 	if (nblocks > RELSEG_SIZE)
+ 	{
+ 		/*
+ 		 * In one relfilenode file length can't be more that RELSEG_SIZE
+ 		 */
+ 		close(fd);
+ 		fprintf(stderr, _("%s: file \"%s\" length is more than segment size: %d.\n"),
+ 				progname, filename, RELSEG_SIZE);
+ 		return false;
+ 	}
+ 
+ 	/*
+ 	 * Read the only page header and find the LSN of the page and continue to
+ 	 * next page.
+ 	 */
+ 	seekpos = 0;
+ 	for (blocknum = 0; blocknum < nblocks; blocknum++)
+ 	{
+ 		len = lseek(fd, seekpos, SEEK_SET);
+ 		if (len != seekpos)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not seek to offset %u in file \"%s\": %s\n"),
+ 					progname, blocknum, filename, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		nbytes = read(fd, buffer, PAGEHDRSZ);
+ 		if (nbytes < 0)
+ 		{
+ 			close(fd);
+ 			fprintf(stderr, _("%s: could not read file \"%s\" at offset %u: %s\n"),
+ 					progname, filename, blocknum, strerror(errno));
+ 			return false;
+ 		}
+ 
+ 		/*
+ 		 * Remember this lsn as the highest (if it is)
+ 		 */
+ 		pagelsn = PageGetLSN(buffer);
+ 		if (filemaxlsn < pagelsn)
+ 		{
+ 			filemaxlsn = pagelsn;
+ 			filemaxlsn_block = blocknum;
+ 		}
+ 
+ 		seekpos += (off_t) BLCKSZ;
+ 	}
+ 
+ 	close(fd);
+ 
+ 	if (*maxlsn < filemaxlsn)
+ 		*maxlsn = filemaxlsn;
+ 
+ 	if (!quiet)
+ 		printf("Highest LSN for file:\"%s\" is: %X/%X in block %u\n",
+ 			   filename, (uint32) (filemaxlsn >> 32), (uint32) filemaxlsn,
+ 			   filemaxlsn_block);
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Read the maximum LSN number in current directory; including sub directories
+  * and links.
+  */
+ static bool
+ FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn)
+ {
+ 	DIR		   *dir;
+ 	struct dirent *de;
+ 	char		pathbuf[MAXPGPATH];
+ 	struct stat statbuf;
+ 	bool		result = true;
+ 
+ 	dir = opendir(path);
+ 	if (NULL == dir)
+ 	{
+ 		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+ 				progname, path, strerror(errno));
+ 		return false;
+ 	}
+ 
+ 	while ((de = readdir(dir)) != NULL)
+ 	{
+ 		/* Skip special stuff */
+ 		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ 			continue;
+ 
+ 		/* Skip temporary files */
+ 		if (strncmp(de->d_name,
+ 					PG_TEMP_FILE_PREFIX,
+ 					strlen(PG_TEMP_FILE_PREFIX)) == 0)
+ 			continue;
+ 
+ 		/*
+ 		 * Skip all invalid files, and read remaining relfilenode files
+ 		 */
+ 		if (!validateRelfilenodename(de->d_name))
+ 			continue;
+ 
+ 		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+ 
+ 		if (lstat(pathbuf, &statbuf) != 0)
+ 		{
+ 			if (errno != ENOENT)
+ 				fprintf(stderr, _("%s: could not stat file or directory \"%s\": %s\n"),
+ 						progname, pathbuf, strerror(errno));
+ 
+ 			/* If the file went away while scanning, it's no error. */
+ 			continue;
+ 		}
+ 
+ 		if (S_ISREG(statbuf.st_mode))
+ 		{
+ 			if (!FindMaxLSNinFile(pathbuf, maxlsn))
+ 				result = false;
+ 		}
+ 		else
+ 		{
+ 			if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 				result = false;
+ 		}
+ 	}
+ 
+ 	closedir(dir);
+ 	return result;
+ }
+ 
+ 
+ /*
+  * Read the maximum LSN number in the DATA directory.
+  */
+ static bool
+ FindMaxLSNinPgData(char *pgdatapath, XLogRecPtr *maxlsn)
+ {
+ 	char		pathbuf[MAXPGPATH];
+ 	bool		result = true;
+ 
+ 	/* scan all the relfilenodes in data directory */
+ 	snprintf(pathbuf, MAXPGPATH, "%s/global", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/base", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	snprintf(pathbuf, MAXPGPATH, "%s/pg_tblspc", pgdatapath);
+ 	if (!FindMaxLSNinDir(pathbuf, maxlsn))
+ 		result = false;
+ 
+ 	return result;
+ }
+ 
+ static void
+ usage(void)
+ {
+ 	printf(_("%s compute the maximum LSN in PostgreSQL data pages.\n\n"), progname);
+ 	printf(_("Usage:\n  %s [OPTION] [FILE/DIRECTORY...]\n\n"), progname);
+ 	printf(_("Options:\n"));
+ 	printf(_("  -q, --quiet        stops the output of highest LSN in a file\n"));
+ 	printf(_("  -V, --version      output version information, then exit\n"));
+ 	printf(_("  -?, --help         show this help, then exit\n"));
+ 	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
+ }
*** a/doc/src/sgml/contrib.sgml
--- b/doc/src/sgml/contrib.sgml
***************
*** 208,212 **** pages.
--- 208,213 ----
   &pgtesttiming;
   &pgupgrade;
   &pgxlogdump;
+  &pgcomputemaxlsn;
   </sect1>
  </appendix>
*** a/doc/src/sgml/filelist.sgml
--- b/doc/src/sgml/filelist.sgml
***************
*** 124,129 ****
--- 124,130 ----
  <!ENTITY pgbench         SYSTEM "pgbench.sgml">
  <!ENTITY pgarchivecleanup SYSTEM "pgarchivecleanup.sgml">
  <!ENTITY pgbuffercache   SYSTEM "pgbuffercache.sgml">
+ <!ENTITY pgcomputemaxlsn   SYSTEM "pg_computemaxlsn.sgml">
  <!ENTITY pgcrypto        SYSTEM "pgcrypto.sgml">
  <!ENTITY pgfreespacemap  SYSTEM "pgfreespacemap.sgml">
  <!ENTITY pgrowlocks      SYSTEM "pgrowlocks.sgml">
*** /dev/null
--- b/doc/src/sgml/pg_computemaxlsn.sgml
***************
*** 0 ****
--- 1,94 ----
+ <!--
+ doc/src/sgml/pg_computemaxlsn.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="pgcomputemaxlsn">
+  <refmeta>
+   <refentrytitle><application>pg_computemaxlsn</application></refentrytitle>
+   <manvolnum>1</manvolnum>
+   <refmiscinfo>Application</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>pg_computemaxlsn</refname>
+   <refpurpose>computes the maximum LSN in database of a <productname>PostgreSQL</productname> database cluster</refpurpose>
+  </refnamediv>
+ 
+  <indexterm zone="pgcomputemaxlsn">
+   <primary>pg_computemaxlsn</primary>
+  </indexterm>
+ 
+  <refsynopsisdiv>
+   <cmdsynopsis>
+    <command>pg_computemaxlsn</command>
+    <arg choice="opt"><replaceable>option</replaceable></arg>
+    <arg rep="repeat"><replaceable>file or directory</replaceable></arg>
+   </cmdsynopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1 id="R1-APP-PGCOMPUTEMAXLSN-1">
+   <title>Description</title>
+   <para>
+    <command>pg_computemaxlsn</command> computes maximun LSN from database pages
+    in the specified list of files or directories.
+   </para>
+ 
+   <para>
+    If user doesn't provide the file or directory to find the max lsn then
+    <command>pg_computemaxlsn</command> use the environment variable <envar>PGDATA</>
+    if exists otherwise reports an error.
+   </para>
+ 
+   <para>
+    The <option>-q</> and <option>--quiet</> options stops the print of
+    highest LSN in a file.
+   </para>
+ 
+   <para>
+    The <option>-V</> and <option>--version</> options
+    print the <application>pg_computemaxlsn</application> version and exit. The
+    options <option>-?</> and <option>--help</> show supported syntax, and exit.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>use cases</title>
+   <para>
+    This utility can be used to avoid more data corruption by finding out the
+    maximum lsn in the data directory which is required by the <command>pg_resetxlog</command>
+    to provide the new WAL segment file name.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+   <screen>
+ pg_computemaxlsn> ./pg_computemaxlsn -q ../../../data/
+ Maximum LSN found in "../../../data/" is: 0/181B090
+ WAL segment file name (fileid,seg): 0/1
+   </screen>
+ 
+   <screen>
+ pg_computemaxlsn> ./pg_computemaxlsn -q ../../../data/base/126*
+ Maximum LSN found in "../../../data/base/12625" is: 0/17D5360
+ WAL segment file name (fileid,seg): 0/1
+ Maximum LSN found in "../../../data/base/12630" is: 0/181B090
+ WAL segment file name (fileid,seg): 0/1
+   </screen>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Notes</title>
+   <para>
+    This utility just parse the header of the page and provides the LSN, please be sure
+    when using this tool on directory which is not a data directory.
+   </para>
+ 
+   <para>
+    If any error occurs during the finding of max LSN in the provided file or directory,
+    the max lsn is not printed. Once the reported problem solves need to run the tool again
+    for proper result.
+   </para>
+  </refsect1>
+ </refentry>
*** a/doc/src/sgml/ref/pg_resetxlog.sgml
--- b/doc/src/sgml/ref/pg_resetxlog.sgml
***************
*** 139,144 **** PostgreSQL documentation
--- 139,153 ----
        largest entry in <filename>pg_xlog</>, use <literal>-l 00000001000000320000004B</> or higher.
       </para>
  
+      <para>
+       If <command>pg_resetxlog</command> complains that it cannot determine
+       valid data for <filename>pg_control</>, and if you do not have or corrupted
+       WAL segment files in the directory <filename>pg_xlog</> under the data directory,
+       then to identify larger WAL segment file from data files we can use utility <command>pg_computemaxlsn</command>
+       by specifing file or folder or data directory. Once larger WAL segment file is found use <option>-l</> option
+       for setting the value.
+      </para>
+ 
       <note>
        <para>
         <command>pg_resetxlog</command> itself looks at the files in
*** a/src/tools/msvc/Mkvcbuild.pm
--- b/src/tools/msvc/Mkvcbuild.pm
***************
*** 43,49 **** my @contrib_uselibpgcommon = (
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
--- 43,49 ----
  	'pg_standby',    'pg_archivecleanup',
  	'pg_test_fsync', 'pg_test_timing',
  	'pg_upgrade',    'pg_xlogdump',
! 	'vacuumlo', 	 'pg_computemaxlsn');
  my $contrib_extralibs = { 'pgbench' => ['wsock32.lib'] };
  my $contrib_extraincludes =
    { 'tsearch2' => ['contrib/tsearch2'], 'dblink' => ['src/backend'] };
#32Amit Kapila
amit.kapila@huawei.com
In reply to: 'Andres Freund' (#30)

On Monday, July 08, 2013 5:16 PM Andres Freund wrote:

On 2013-07-08 17:10:43 +0530, Amit Kapila wrote:

On Monday, July 08, 2013 4:26 PM Andres Freund wrote:

On 2013-07-08 16:17:54 +0530, Hari Babu wrote:

+ This utility can also be used to decide whether backup is

required or not when the data page

+ in old-master precedes the last applied LSN in old-standby

(i.e., new-master) at the

+    moment of the failover.
+   </para>
+  </refsect1>

I don't think this is safe in any interesting set of cases. Am I
missing
something?

No, you are not missing anything. It can be only used to find max LSN

in

database which can avoid further corruption

Why is the patch submitted documenting it as a use-case then?

This is my mistake, I was not able to catch.
I am really sorry for it and in future will make sure such mistake doesn't
happen again

I find it
rather scary if the *patch authors* document a known unsafe use case as
one of the known use-cases.

With Regards,
Amit Kapila.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers