From e0d17040909335225aa33dcd32ec8f0308ae6d4f Mon Sep 17 00:00:00 2001
From: Ashutosh Sharma <ashu.coek88@gmail.com>
Date: Thu, 22 Sep 2022 16:37:32 +0000
Subject: [PATCH] Enhance pg_waldump to show latest LSN and the WAL file that
 the latest LSN belongs to.

When -l/--lastLSN is passed as an argument along with datadir, pg_waldump
returns the last LSN aka EndRecPtr along with the WAL file name that the
last LSN belongs to.
---
 src/bin/pg_waldump/pg_waldump.c | 133 +++++++++++++++++++++++++++++++-
 1 file changed, 132 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 9993378ca5..f15f451f3d 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -22,6 +22,7 @@
 #include "access/xlogreader.h"
 #include "access/xlogrecord.h"
 #include "access/xlogstats.h"
+#include "catalog/pg_control.h"
 #include "common/fe_memutils.h"
 #include "common/logging.h"
 #include "getopt_long.h"
@@ -57,6 +58,7 @@ typedef struct XLogDumpConfig
 	bool		follow;
 	bool		stats;
 	bool		stats_per_record;
+	bool		output_lastLSN;
 
 	/* filter options */
 	bool		filter_by_rmgr[RM_MAX_ID + 1];
@@ -652,6 +654,76 @@ XLogDumpDisplayStats(XLogDumpConfig *config, XLogStats *stats)
 		   total_len, "[100%]");
 }
 
+/*
+ * Get controlfile values. The result is returned as a palloc'd copy of the
+ * control file data.
+ *
+ * crc_ok can be used by the caller to see whether the CRC of the control
+ * file data is correct.
+ */
+static ControlFileData *
+get_control_file(char *datadir, bool *crc_ok)
+{
+	ControlFileData *ControlFile;
+	int			fd;
+	char		ControlFilePath[MAXPGPATH];
+	pg_crc32c	crc;
+	int			r;
+
+	AssertArg(crc_ok);
+
+	ControlFile = palloc(sizeof(ControlFileData));
+	snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", datadir);
+
+	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
+	{
+		pg_fatal("could not open file \"%s\" for reading: %m",
+				 ControlFilePath);
+		exit(EXIT_FAILURE);
+	}
+
+	r = read(fd, ControlFile, sizeof(ControlFileData));
+	if (r != sizeof(ControlFileData))
+	{
+		if (r < 0)
+		{
+			pg_fatal("could not read file \"%s\": %m", ControlFilePath);
+			exit(EXIT_FAILURE);
+		}
+		else
+		{
+			pg_fatal("could not read file \"%s\": read %d of %zu",
+					 ControlFilePath, r, sizeof(ControlFileData));
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (close(fd) != 0)
+	{
+		pg_fatal("could not close file \"%s\": %m", ControlFilePath);
+		exit(EXIT_FAILURE);
+	}
+
+	/* Check the CRC. */
+	INIT_CRC32C(crc);
+	COMP_CRC32C(crc,
+				(char *) ControlFile,
+				offsetof(ControlFileData, crc));
+	FIN_CRC32C(crc);
+
+	*crc_ok = EQ_CRC32C(crc, ControlFile->crc);
+
+	/* Make sure the control file is valid byte order. */
+	if (ControlFile->pg_control_version % 65536 == 0 &&
+		ControlFile->pg_control_version / 65536 != 0)
+		pg_log_warning("possible byte ordering mismatch\n"
+					   "The byte ordering used to store the pg_control file might not match the one\n"
+					   "used by this program.  In that case the results below would be incorrect, and\n"
+					   "the PostgreSQL installation would be incompatible with this data directory.");
+
+	return ControlFile;
+}
+
 static void
 usage(void)
 {
@@ -662,10 +734,12 @@ usage(void)
 	printf(_("\nOptions:\n"));
 	printf(_("  -b, --bkp-details      output detailed information about backup blocks\n"));
 	printf(_("  -B, --block=N          with --relation, only show records that modify block N\n"));
+	printf(_("  -D, --datadir=DATADIR  location of data directory\n"));
 	printf(_("  -e, --end=RECPTR       stop reading at WAL location RECPTR\n"));
 	printf(_("  -f, --follow           keep retrying after reaching end of WAL\n"));
 	printf(_("  -F, --fork=FORK        only show records that modify blocks in fork FORK;\n"
 			 "                         valid names are main, fsm, vm, init\n"));
+	printf(_("  -l, --lastLSN          output latest LSN in the wal directory, then exit\n"));
 	printf(_("  -n, --limit=N          number of records to display\n"));
 	printf(_("  -p, --path=PATH        directory in which to find WAL segment files or a\n"
 			 "                         directory with a ./pg_wal that contains such files\n"
@@ -699,16 +773,19 @@ main(int argc, char **argv)
 	XLogRecord *record;
 	XLogRecPtr	first_record;
 	char	   *waldir = NULL;
+	char	   *datadir = NULL;
 	char	   *errormsg;
 
 	static struct option long_options[] = {
 		{"bkp-details", no_argument, NULL, 'b'},
 		{"block", required_argument, NULL, 'B'},
+		{"datadir", required_argument, NULL, 'D'},
 		{"end", required_argument, NULL, 'e'},
 		{"follow", no_argument, NULL, 'f'},
 		{"fork", required_argument, NULL, 'F'},
 		{"fullpage", no_argument, NULL, 'w'},
 		{"help", no_argument, NULL, '?'},
+		{"lastLSN", no_argument, NULL, 'l'},
 		{"limit", required_argument, NULL, 'n'},
 		{"path", required_argument, NULL, 'p'},
 		{"quiet", no_argument, NULL, 'q'},
@@ -761,6 +838,7 @@ main(int argc, char **argv)
 	config.stop_after_records = -1;
 	config.already_displayed_records = 0;
 	config.follow = false;
+	config.output_lastLSN = false;
 	/* filter_by_rmgr array was zeroed by memset above */
 	config.filter_by_rmgr_enabled = false;
 	config.filter_by_xid = InvalidTransactionId;
@@ -782,7 +860,7 @@ main(int argc, char **argv)
 		goto bad_argument;
 	}
 
-	while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
+	while ((option = getopt_long(argc, argv, "bB:D:e:fF:ln:p:qr:R:s:t:wx:z",
 								 long_options, &optindex)) != -1)
 	{
 		switch (option)
@@ -800,6 +878,10 @@ main(int argc, char **argv)
 				config.filter_by_relation_block_enabled = true;
 				config.filter_by_extended = true;
 				break;
+			case 'D':
+				datadir = pg_strdup(optarg);
+				setenv("PGDATA", datadir, 0);
+				break;
 			case 'e':
 				if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
 				{
@@ -821,6 +903,9 @@ main(int argc, char **argv)
 				}
 				config.filter_by_extended = true;
 				break;
+			case 'l':
+				config.output_lastLSN = true;
+				break;
 			case 'n':
 				if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
 				{
@@ -947,6 +1032,12 @@ main(int argc, char **argv)
 		}
 	}
 
+	if (config.output_lastLSN && !datadir)
+	{
+		pg_log_error("option -l/--lastLSN requires option -D/--datadir to be specified");
+		goto bad_argument;
+	}
+
 	if (config.filter_by_relation_block_enabled &&
 		!config.filter_by_relation_enabled)
 	{
@@ -972,6 +1063,28 @@ main(int argc, char **argv)
 		}
 	}
 
+	if (config.output_lastLSN)
+	{
+		ControlFileData	*ControlFile;
+		bool	crc_ok;
+
+		/* read the control file */
+		ControlFile = get_control_file(datadir, &crc_ok);
+
+		if (!crc_ok)
+		{
+			pg_fatal("Calculated CRC checksum does not match value stored in file.");
+			exit(EXIT_FAILURE);
+		}
+
+		private.timeline = ControlFile->checkPointCopy.ThisTimeLineID;
+		private.startptr = ControlFile->checkPointCopy.redo;
+		private.endptr = InvalidXLogRecPtr;
+		waldir = identify_target_directory(waldir, NULL);
+		config.quiet = true;
+		goto start_reading_wal;
+	}
+
 	/* parse files as start/end boundaries, extract path if not specified */
 	if (optind < argc)
 	{
@@ -1064,6 +1177,7 @@ main(int argc, char **argv)
 	/* done with argument parsing, do the actual work */
 
 	/* we have everything we need, start reading */
+start_reading_wal:
 	xlogreader_state =
 		XLogReaderAllocate(WalSegSz, waldir,
 						   XL_ROUTINE(.page_read = WALDumpReadPage,
@@ -1109,6 +1223,23 @@ main(int argc, char **argv)
 		record = XLogReadRecord(xlogreader_state, &errormsg);
 		if (!record)
 		{
+			if (config.output_lastLSN)
+			{
+				XLogSegNo   xlogsegno;
+				char        xlogfilename[MAXFNAMELEN];
+
+				XLByteToPrevSeg(xlogreader_state->EndRecPtr, xlogsegno, WalSegSz);
+				XLogFileName(xlogfilename, private.timeline, xlogsegno,
+							 WalSegSz);
+
+				printf("Latest LSN: %X/%X\n"
+					   "Latest WAL filename: %s\n",
+					   LSN_FORMAT_ARGS(xlogreader_state->EndRecPtr),
+					   xlogfilename);
+
+				exit(EXIT_SUCCESS);
+			}
+
 			if (!config.follow || private.endptr_reached)
 				break;
 			else
-- 
2.17.1

