binary version of pg_current_wal_insert_lsn and pg_walfile_name functions
Hi All,
Currently, we have pg_current_wal_insert_lsn and pg_walfile_name sql
functions which gives us information about the next wal insert
location and the WAL file that the next wal insert location belongs
to. Can we have a binary version of these sql functions? It would be
like any other binaries we have for e.g. pg_waldump to which we can
pass the location of the pg_wal directory. This binary would scan
through the directory to return the next wal insert location and the
wal file the next wal insert pointer belongs to.
The binary version of these sql functions can be used when the server
is offline. This can help us to know the overall WAL data that needs
to be replayed when the server is in recovery. In the control file we
do have the redo pointer. Knowing the end pointer would definitely be
helpful.
If you are ok then I will prepare a patch for it and share it. Please
let me know your thoughts/comments. thank you.!
--
With Regards,
Ashutosh Sharma.
On Mon, Sep 19, 2022 at 8:19 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
Hi All,
Currently, we have pg_current_wal_insert_lsn and pg_walfile_name sql
functions which gives us information about the next wal insert
location and the WAL file that the next wal insert location belongs
to. Can we have a binary version of these sql functions?
+1 for the idea in general.
As said, pg_waldump seems to be the right candidate. I think we want
the lsn of the last WAL record and its info and the WAL file name
given an input data directory or just the pg_wal directory or any
directory where WAL files are located. For instance, one can use this
on an archive location containing archived WAL files or on a node
where pg_receivewal is receiving WAL files. Am I missing any other
use-cases?
pg_waldump currently can't understand compressed and partial files. I
think that we need to fix this as well.
--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
On Tue, Sep 20, 2022 at 5:13 PM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
On Mon, Sep 19, 2022 at 8:19 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
Hi All,
Currently, we have pg_current_wal_insert_lsn and pg_walfile_name sql
functions which gives us information about the next wal insert
location and the WAL file that the next wal insert location belongs
to. Can we have a binary version of these sql functions?+1 for the idea in general.
As said, pg_waldump seems to be the right candidate. I think we want
the lsn of the last WAL record and its info and the WAL file name
given an input data directory or just the pg_wal directory or any
directory where WAL files are located. For instance, one can use this
on an archive location containing archived WAL files or on a node
where pg_receivewal is receiving WAL files. Am I missing any other
use-cases?
Yeah, we can either add this functionality to pg_waldump or maybe add
a new binary itself that would return this information.
--
With Regards,
Ashutosh Sharma.
On Wed, Sep 21, 2022 at 9:53 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
Yeah, we can either add this functionality to pg_waldump or maybe add
a new binary itself that would return this information.
IMV, a separate tool isn't the way, since pg_waldump already reads WAL
files and decodes WAL records, what's proposed here can be an
additional functionality of pg_waldump.
It will be great if an initial patch is posted here.
--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
On Thu, Sep 22, 2022 at 7:41 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
On Wed, Sep 21, 2022 at 9:53 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
Yeah, we can either add this functionality to pg_waldump or maybe add
a new binary itself that would return this information.IMV, a separate tool isn't the way, since pg_waldump already reads WAL
files and decodes WAL records, what's proposed here can be an
additional functionality of pg_waldump.It will be great if an initial patch is posted here.
PFA that enhances pg_waldump to show the latest LSN and the
corresponding WAL file when the -l or --lastLSN option is passed an
argument to pg_waldump. Below is an example:
ashu@92893de650ed:~/pgsql$ pg_waldump -l -D ./data-dir
Latest LSN: 0/148A45F8
Latest WAL filename: 000000010000000000000014
How has it been coded?
When the user passes the '-l' command line option along with the data
directory path to pg_waldump, it reads the control file from the data
directory. From the control file, it gets information like redo
pointer and current timeline id. The redo pointer is considered to be
the start pointer from where the pg_waldump starts reading wal data
until end-of-wal to find the last LSN. For details please check the
attached patch.
Please note that for compressed and .partial wal files this doesn't work.
--
With Regards,
Ashutosh Sharma.
Attachments:
0001-Enhance-pg_waldump-to-show-latest-LSN-and-the-WAL-fi.patchapplication/octet-stream; name=0001-Enhance-pg_waldump-to-show-latest-LSN-and-the-WAL-fi.patchDownload
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
On Thu, Sep 22, 2022 at 10:25 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
PFA that enhances pg_waldump to show the latest LSN and the
corresponding WAL file when the -l or --lastLSN option is passed an
argument to pg_waldump. Below is an example:
Thanks for the patch. I have some quick thoughts about it.
When the user passes the '-l' command line option along with the data
directory path to pg_waldump, it reads the control file from the data
directory.
I don't think we need a new option for data directory -D. pg_waldump's
option 'p' can be used, please see the comments around
identify_target_directory().
From the control file, it gets information like redo
pointer and current timeline id.
Is there any reason for not using get_control_file() from
src/common/controldata_utils.c, but defining the exact same function
in pg_waldump.c?
The redo pointer is considered to be
the start pointer from where the pg_waldump starts reading wal data
until end-of-wal to find the last LSN. For details please check the
attached patch.
Making it dependent on the controlfile limits the usability of this
feature. Imagine, using this feature on an archive location or
pg_receivewal target directory where there are WAL files but no
controlfile. I think we can choose the appropriate combinations of
existing pg_waldump options, for instance, let users enter the start
WAL segment via startseg and/or start LSN via --start and the new
option for end WAL segment and end LSN.
Please note that for compressed and .partial wal files this doesn't work.
Looking forward to the above capability because it expands the
usability of this feature.
--
Bharath Rupireddy
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
On Fri, Sep 23, 2022 at 6:05 AM Bharath Rupireddy
<bharath.rupireddyforpostgres@gmail.com> wrote:
On Thu, Sep 22, 2022 at 10:25 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
PFA that enhances pg_waldump to show the latest LSN and the
corresponding WAL file when the -l or --lastLSN option is passed an
argument to pg_waldump. Below is an example:Thanks for the patch. I have some quick thoughts about it.
When the user passes the '-l' command line option along with the data
directory path to pg_waldump, it reads the control file from the data
directory.I don't think we need a new option for data directory -D. pg_waldump's
option 'p' can be used, please see the comments around
identify_target_directory().
-p is the path to the WAL directory. It doesn't necessarily have to be
a data directory, however the user can specify the data directory path
here as well using which the path to the WAL directory can be
recognized, but as I said it doesn't mean -p will always represent the
data directory.
From the control file, it gets information like redo
pointer and current timeline id.Is there any reason for not using get_control_file() from
src/common/controldata_utils.c, but defining the exact same function
in pg_waldump.c?
Will give it a thought on it later. If possible, will try to reuse it.
The redo pointer is considered to be
the start pointer from where the pg_waldump starts reading wal data
until end-of-wal to find the last LSN. For details please check the
attached patch.Making it dependent on the controlfile limits the usability of this
feature. Imagine, using this feature on an archive location or
pg_receivewal target directory where there are WAL files but no
controlfile. I think we can choose the appropriate combinations of
existing pg_waldump options, for instance, let users enter the start
WAL segment via startseg and/or start LSN via --start and the new
option for end WAL segment and end LSN.
I have written this patch assuming that the end user is not aware of
any LSN or any other WAL data and wants to know the last LSN. So all
he can do is take the help of the control data to find the redo LSN
and use that as a reference point (start pointer) to find the last
LSN. And whatever is the WAL directory (be it archive location or wall
collected via pg_receivewal or pg_wal directory), we will consider the
redo pointer as the start pointer. Now, it's possible that the WAL
corresponding to the start pointer is not at all available in the WAL
directory like archive location or pg_receivewal directory in which
this cannot be used, but this is very unlikely.
Please note that for compressed and .partial wal files this doesn't work.
Looking forward to the above capability because it expands the
usability of this feature.
This is a different task altogether. We will probably need to work on
it separately.
--
With Regards,
Ashutosh Sharma.
PFA v2 patch.
Changes in the v2 patch:
- Reuse the existing get_controlfile function in
src/common/controldata_utils.c instead of adding a new one.
- Set env variable PGDATA with the data directory specified by the user.
--
With Regards,
Ashutosh Sharma.
On Fri, Sep 23, 2022 at 12:24 PM Ashutosh Sharma <ashu.coek88@gmail.com> wrote:
PFA v2 patch.
Changes in the v2 patch:
- Reuse the existing get_controlfile function in
src/common/controldata_utils.c instead of adding a new one.- Set env variable PGDATA with the data directory specified by the user.
Forgot to attach the patch with above changes. Here it is.
--
With Regards,
Ashutosh Sharma.
Attachments:
v2-0001-Enhance-pg_waldump-to-show-latest-LSN-and-the-corrre.patchapplication/octet-stream; name=v2-0001-Enhance-pg_waldump-to-show-latest-LSN-and-the-corrre.patchDownload
From 1571a39ecfcc97607d3202d112b7a401ee758385 Mon Sep 17 00:00:00 2001
From: Ashutosh Sharma <ashu.coek88@gmail.com>
Date: Fri, 23 Sep 2022 06:40:32 +0000
Subject: [PATCH] Enhance pg_waldump to show latest LSN and the corrresponding
WAL file.
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.
Usage:
ashu@ubuntu:~/pgsql$ pg_waldump -l -D data-dir
Latest LSN: 0/148A45F8
Latest WAL filename: 000000010000000000000014
---
src/bin/pg_waldump/pg_waldump.c | 64 ++++++++++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index 9993378ca5..cb784362da 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -22,6 +22,8 @@
#include "access/xlogreader.h"
#include "access/xlogrecord.h"
#include "access/xlogstats.h"
+#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
#include "common/fe_memutils.h"
#include "common/logging.h"
#include "getopt_long.h"
@@ -57,6 +59,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];
@@ -662,10 +665,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 +704,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 +769,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 +791,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 +809,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, 1);
+ break;
case 'e':
if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
{
@@ -821,6 +834,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 +963,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 +994,28 @@ main(int argc, char **argv)
}
}
+ if (config.output_lastLSN)
+ {
+ ControlFileData *ControlFile;
+ bool crc_ok;
+
+ /* read the control file */
+ ControlFile = get_controlfile(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 +1108,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 +1154,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