pg_resetxlog_find_max_lsn_from_datafiles.patch

text/plain

Filename: pg_resetxlog_find_max_lsn_from_datafiles.patch
Type: text/plain
Part: 0
Message: [PATCH] Patch to compute Max LSN of Data Pages

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/bin/pg_resetxlog/pg_resetxlog.c 366 1
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index d5d89ec..dcb62d1 100644
--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -54,6 +54,20 @@
 #include "access/xlog_internal.h"
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
+#include "catalog/catalog.h"
+#include "storage/bufpage.h"
+#include "storage/fd.h"
+
+
+/* Page header size */
+#define PAGEHDRSZ (sizeof(PageHeaderData))
+
+
+/*
+ * relfile nodename validation allow only file name start with digit
+ */
+#define validateRelfilenodename(name) ((name[0] >= '0') && (name[0] <= '9'))
+
 
 extern int	optind;
 extern char *optarg;
@@ -72,6 +86,9 @@ static void FindEndOfXLOG(void);
 static void KillExistingXLOG(void);
 static void KillExistingArchiveStatus(void);
 static void WriteEmptyXLOG(void);
+static void FindMaxLSNinFile(char *filename, XLogRecPtr *maxlsn);
+static void FindMaxLSNinDir(char *path, XLogRecPtr *maxlsn);
+static void FindMaxLSNinPgData(XLogRecPtr *maxlsn);
 static void usage(void);
 
 
@@ -92,6 +109,10 @@ main(int argc, char *argv[])
 	char	   *DataDir;
 	int			fd;
 	char		path[MAXPGPATH];
+	bool		print_max_lsn = false;
+	bool		print_pgdata_max_lsn = false;
+	char	   *LsnSearchPath;
+	uint64		maxLSN = 0;
 
 	set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_resetxlog"));
 
@@ -112,7 +133,7 @@ main(int argc, char *argv[])
 	}
 
 
-	while ((c = getopt(argc, argv, "fl:m:no:O:x:e:")) != -1)
+	while ((c = getopt(argc, argv, "fl:m:no:O:x:e:p:P")) != -1)
 	{
 		switch (c)
 		{
@@ -209,6 +230,15 @@ main(int argc, char *argv[])
 				XLogFromFileName(optarg, &minXlogTli, &minXlogSegNo);
 				break;
 
+			case 'p':
+				print_max_lsn = true;
+				LsnSearchPath = optarg;
+				break;
+
+			case 'P':
+				print_pgdata_max_lsn = true;
+				break;
+
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
 				exit(1);
@@ -270,6 +300,55 @@ main(int argc, char *argv[])
 		exit(1);
 	}
 
+
+	if (print_max_lsn || print_pgdata_max_lsn)
+	{
+		XLogSegNo logSegNo = 0;
+		
+		if (print_pgdata_max_lsn)
+		{
+			FindMaxLSNinPgData(&maxLSN);
+		}
+		else
+		{
+			struct stat fst;
+
+			if (stat(LsnSearchPath, &fst) < 0)
+			{
+				if (errno == ENOENT)
+				{
+					fprintf(stderr, _("%s: file or directory \"%s\" does not exists"),
+							progname, LsnSearchPath);
+				}
+				else
+				{
+					fprintf(stderr, _("%s: file or directory \"%s\" exists\n"
+									  "\n"),
+							progname, LsnSearchPath);
+				}
+				exit(1);
+			}
+
+			if (S_ISDIR(fst.st_mode))
+			{
+				FindMaxLSNinDir(LsnSearchPath, &maxLSN);
+			}
+			else
+			{
+				FindMaxLSNinFile(LsnSearchPath, &maxLSN);
+			}
+		}
+
+		XLByteToSeg(maxLSN, logSegNo);
+
+		printf ("Maximum LSN found is: " UINT64_FORMAT ", "
+				"WAL segment file name (fileid,seg): %08X%08X\n",
+				maxLSN,
+				(uint32) ((logSegNo) / XLogSegmentsPerXLogId),
+				(uint32) ((logSegNo) % XLogSegmentsPerXLogId));
+		exit(0);
+	}
+
 	/*
 	 * Attempt to read the existing pg_control file
 	 */
@@ -986,6 +1065,290 @@ WriteEmptyXLOG(void)
 }
 
 
+/*
+ * 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 nex 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 void
+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 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;
+	}
+
+	/* 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;
+	}
+
+	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\" legth is more than RELSEG_SIZE.\n"),
+				progname, filename);
+		return;
+	}
+
+	/*
+	 * 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;
+		}
+
+		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;
+		}
+
+		if (PageHeaderIsValid((PageHeader) buffer))
+		{
+			pagelsn = PageGetLSN(buffer);
+			if (XLByteLE(*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;
+}
+
+/*
+ * Read the maximum LSN number in current directory; ignore sub directories.
+ */
+static void
+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;
+	}
+
+	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 (!validateRelfilenodename(de->d_name))
+			continue;
+
+		snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name);
+
+		if (stat(pathbuf, &statbuf) != 0)
+		{
+			if (errno != ENOENT)
+			{
+				fprintf(stderr, "could not stat file or directory \"%s\"",
+						pathbuf);
+			}
+			/* If the file went away while scanning, it's no error. */
+			continue;
+		}
+
+		/*
+		 * Skip all directories
+		 */
+		if (!S_ISDIR(statbuf.st_mode))
+		{
+			FindMaxLSNinFile(pathbuf, maxlsn);
+		}
+	}
+
+	closedir(dir);
+	return;
+}
+
+/*
+ * Read the maximum LSN number in the DATA directory.
+ */
+static void
+FindMaxLSNinPgData(XLogRecPtr *maxlsn)
+{
+	DIR		   *dir,
+			   *dir2;
+	struct dirent *de,
+			   *de2;
+	char		pathbuf[MAXPGPATH],
+				pathbuf2[MAXPGPATH];
+
+	/*
+	 * scan all the relfilenodes in global directory
+	 */
+	FindMaxLSNinDir("global", maxlsn);
+
+	/*
+	 * scan all the relfilenodes in base directory like base/<database-id>
+	 */
+	dir = opendir("base");
+	if (NULL == dir)
+	{
+		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+				progname, "base", strerror(errno));
+		return;
+	}
+
+	while ((de = readdir(dir)) != NULL)
+	{
+		/* Skip special stuff */
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+
+		/* Skip template database */
+		if (strcmp(de->d_name, "0") == 0 || strcmp(de->d_name, "1") == 0)
+			continue;
+
+		snprintf(pathbuf, MAXPGPATH, "%s/%s", "base", de->d_name);
+
+		FindMaxLSNinDir(pathbuf, maxlsn);
+	}
+	closedir(dir);
+
+	/* Scan all tablespaces */
+	dir = opendir("pg_tblspc");
+	if (NULL == dir)
+	{
+		fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+				progname, "pg_tblspc", strerror(errno));
+		return;
+	}
+
+	while ((de = readdir(dir)) != NULL)
+	{
+		/* Skip special stuff */
+		if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+			continue;
+
+		/*
+		 * Scan all directories in tablespace
+		 * ./pg_tblspc/tablespaceid/TABLESPACE_VERSION_DIRECTORY/ *
+		 */
+		snprintf(pathbuf, MAXPGPATH, "%s/%s/%s", "pg_tblspc", de->d_name, TABLESPACE_VERSION_DIRECTORY);
+		dir2 = opendir(pathbuf);
+		if (NULL == dir2)
+		{
+			fprintf(stderr, _("%s: could not open directory \"%s\": %s\n"),
+					progname, pathbuf, strerror(errno));
+			return;
+		}
+
+		while ((de2 = readdir(dir2)) != NULL)
+		{
+			/* Skip special stuff */
+			if (strcmp(de2->d_name, ".") == 0 || strcmp(de2->d_name, "..") == 0)
+				continue;
+
+			snprintf(pathbuf2, MAXPGPATH, "%s/%s", pathbuf, de2->d_name);
+
+			FindMaxLSNinDir(pathbuf2, maxlsn);
+		}
+		closedir(dir2);
+	}
+	closedir(dir);
+
+	return;
+}
+
 static void
 usage(void)
 {
@@ -997,6 +1360,8 @@ usage(void)
 	printf(_("  -l xlogfile      force minimum WAL starting location for new transaction log\n"));
 	printf(_("  -m XID           set next multitransaction ID\n"));
 	printf(_("  -n               no update, just show extracted control values (for testing)\n"));
+	printf(_("  -p {file | dir}  print max LSN from specified file or directory path\n"));
+	printf(_("  -P               print max LSN from whole database\n"));
 	printf(_("  -o OID           set next OID\n"));
 	printf(_("  -O OFFSET        set next multitransaction offset\n"));
 	printf(_("  -V, --version    output version information, then exit\n"));