From 9d63a01bc634bbb899ce9f4645bf677bcbaec998 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Wed, 8 Mar 2023 07:39:36 +0000
Subject: [PATCH v3] Rework pg_walinspect functions

---
 contrib/pg_walinspect/Makefile                |   2 +-
 .../pg_walinspect/expected/oldextversions.out |  64 +++++
 .../pg_walinspect/expected/pg_walinspect.out  |  87 ++++++-
 contrib/pg_walinspect/meson.build             |   1 +
 .../pg_walinspect/pg_walinspect--1.0--1.1.sql |  57 ++++-
 contrib/pg_walinspect/pg_walinspect.c         | 235 +++++++++++-------
 contrib/pg_walinspect/sql/oldextversions.sql  |  27 ++
 contrib/pg_walinspect/sql/pg_walinspect.sql   |  51 +++-
 doc/src/sgml/pgwalinspect.sgml                |  71 ++----
 9 files changed, 442 insertions(+), 153 deletions(-)
 create mode 100644 contrib/pg_walinspect/expected/oldextversions.out
 create mode 100644 contrib/pg_walinspect/sql/oldextversions.sql

diff --git a/contrib/pg_walinspect/Makefile b/contrib/pg_walinspect/Makefile
index 7033878a79..22090f7716 100644
--- a/contrib/pg_walinspect/Makefile
+++ b/contrib/pg_walinspect/Makefile
@@ -9,7 +9,7 @@ PGFILEDESC = "pg_walinspect - functions to inspect contents of PostgreSQL Write-
 EXTENSION = pg_walinspect
 DATA = pg_walinspect--1.0.sql pg_walinspect--1.0--1.1.sql
 
-REGRESS = pg_walinspect
+REGRESS = pg_walinspect oldextversions
 
 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_walinspect/walinspect.conf
 
diff --git a/contrib/pg_walinspect/expected/oldextversions.out b/contrib/pg_walinspect/expected/oldextversions.out
new file mode 100644
index 0000000000..c3ebcc2b49
--- /dev/null
+++ b/contrib/pg_walinspect/expected/oldextversions.out
@@ -0,0 +1,64 @@
+-- test old extension version entry points
+DROP EXTENSION pg_walinspect;
+CREATE EXTENSION pg_walinspect WITH VERSION '1.0';
+-- Move to new version 1.1
+ALTER EXTENSION pg_walinspect UPDATE TO '1.1';
+-- New function introduced in 1.1
+SELECT pg_get_functiondef('pg_get_wal_fpi_info'::regproc);
+                                                                                                                     pg_get_functiondef                                                                                                                     
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.pg_get_wal_fpi_info(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL::pg_lsn, OUT lsn pg_lsn, OUT reltablespace oid, OUT reldatabase oid, OUT relfilenode oid, OUT relblocknumber bigint, OUT forkname text, OUT fpi bytea)+
+  RETURNS SETOF record                                                                                                                                                                                                                                     +
+  LANGUAGE c                                                                                                                                                                                                                                               +
+  PARALLEL SAFE                                                                                                                                                                                                                                            +
+ AS '$libdir/pg_walinspect', $function$pg_get_wal_fpi_info$function$                                                                                                                                                                                       +
+ 
+(1 row)
+
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_records_info'::regproc);
+                                                                                                                                                                            pg_get_functiondef                                                                                                                                                                            
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.pg_get_wal_records_info(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL::pg_lsn, OUT start_lsn pg_lsn, OUT end_lsn pg_lsn, OUT prev_lsn pg_lsn, OUT xid xid, OUT resource_manager text, OUT record_type text, OUT record_length integer, OUT main_data_length integer, OUT fpi_length integer, OUT description text, OUT block_ref text)+
+  RETURNS SETOF record                                                                                                                                                                                                                                                                                                                                                   +
+  LANGUAGE c                                                                                                                                                                                                                                                                                                                                                             +
+  PARALLEL SAFE                                                                                                                                                                                                                                                                                                                                                          +
+ AS '$libdir/pg_walinspect', $function$pg_get_wal_records_info$function$                                                                                                                                                                                                                                                                                                 +
+ 
+(1 row)
+
+-- Removed function
+SELECT pg_get_functiondef('pg_get_wal_records_info_till_end_of_wal'::regproc);
+ERROR:  function "pg_get_wal_records_info_till_end_of_wal" does not exist
+LINE 1: SELECT pg_get_functiondef('pg_get_wal_records_info_till_end_...
+                                  ^
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_stats'::regproc);
+                                                                                                                                                                                                                     pg_get_functiondef                                                                                                                                                                                                                     
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.pg_get_wal_stats(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL::pg_lsn, per_record boolean DEFAULT false, OUT "resource_manager/record_type" text, OUT count bigint, OUT count_percentage double precision, OUT record_size bigint, OUT record_size_percentage double precision, OUT fpi_size bigint, OUT fpi_size_percentage double precision, OUT combined_size bigint, OUT combined_size_percentage double precision)+
+  RETURNS SETOF record                                                                                                                                                                                                                                                                                                                                                                                                                                     +
+  LANGUAGE c                                                                                                                                                                                                                                                                                                                                                                                                                                               +
+  PARALLEL SAFE                                                                                                                                                                                                                                                                                                                                                                                                                                            +
+ AS '$libdir/pg_walinspect', $function$pg_get_wal_stats$function$                                                                                                                                                                                                                                                                                                                                                                                          +
+ 
+(1 row)
+
+-- Removed function
+SELECT pg_get_functiondef('pg_get_wal_stats_till_end_of_wal'::regproc);
+ERROR:  function "pg_get_wal_stats_till_end_of_wal" does not exist
+LINE 1: SELECT pg_get_functiondef('pg_get_wal_stats_till_end_of_wal'...
+                                  ^
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_fpi_info'::regproc);
+                                                                                                                     pg_get_functiondef                                                                                                                     
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE OR REPLACE FUNCTION public.pg_get_wal_fpi_info(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL::pg_lsn, OUT lsn pg_lsn, OUT reltablespace oid, OUT reldatabase oid, OUT relfilenode oid, OUT relblocknumber bigint, OUT forkname text, OUT fpi bytea)+
+  RETURNS SETOF record                                                                                                                                                                                                                                     +
+  LANGUAGE c                                                                                                                                                                                                                                               +
+  PARALLEL SAFE                                                                                                                                                                                                                                            +
+ AS '$libdir/pg_walinspect', $function$pg_get_wal_fpi_info$function$                                                                                                                                                                                       +
+ 
+(1 row)
+
+DROP EXTENSION pg_walinspect;
diff --git a/contrib/pg_walinspect/expected/pg_walinspect.out b/contrib/pg_walinspect/expected/pg_walinspect.out
index 9bcb05354e..71dcd8cfbb 100644
--- a/contrib/pg_walinspect/expected/pg_walinspect.out
+++ b/contrib/pg_walinspect/expected/pg_walinspect.out
@@ -14,38 +14,99 @@ INSERT INTO sample_tbl SELECT * FROM generate_series(3, 4);
 -- ===================================================================
 -- Tests for input validation
 -- ===================================================================
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info(:'wal_lsn2', :'wal_lsn1'); -- ERROR
-ERROR:  WAL start LSN must be less than end LSN
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats(:'wal_lsn2', :'wal_lsn1'); -- ERROR
-ERROR:  WAL start LSN must be less than end LSN
+-- Invalid input LSNs
+SELECT * FROM pg_get_wal_record_info('0/0'); -- ERROR
+ERROR:  invalid input LSN
+SELECT * FROM pg_get_wal_records_info('0/0'); -- ERROR
+ERROR:  invalid start LSN
+SELECT * FROM pg_get_wal_stats('0/0'); -- ERROR
+ERROR:  invalid start LSN
+SELECT * FROM pg_get_wal_fpi_info('0/0'); -- ERROR
+ERROR:  invalid start LSN
+-- Start LSN NULL
+SELECT * FROM pg_get_wal_records_info(NULL); -- ERROR
+ERROR:  invalid start LSN
+SELECT * FROM pg_get_wal_stats(NULL); -- ERROR
+ERROR:  invalid start LSN
+SELECT * FROM pg_get_wal_fpi_info(NULL); -- ERROR
+ERROR:  invalid start LSN
+-- Start LSN > End LSN
+SELECT * FROM pg_get_wal_records_info(:'wal_lsn2', :'wal_lsn1'); -- NULL
+ start_lsn | end_lsn | prev_lsn | xid | resource_manager | record_type | record_length | main_data_length | fpi_length | description | block_ref 
+-----------+---------+----------+-----+------------------+-------------+---------------+------------------+------------+-------------+-----------
+           |         |          |     |                  |             |               |                  |            |             | 
+(1 row)
+
+SELECT * FROM pg_get_wal_stats(:'wal_lsn2', :'wal_lsn1'); -- NULL
+ resource_manager/record_type | count | count_percentage | record_size | record_size_percentage | fpi_size | fpi_size_percentage | combined_size | combined_size_percentage 
+------------------------------+-------+------------------+-------------+------------------------+----------+---------------------+---------------+--------------------------
+                              |       |                  |             |                        |          |                     |               |                         
+(1 row)
+
+SELECT * FROM pg_get_wal_fpi_info(:'wal_lsn2', :'wal_lsn1'); -- NULL
+ lsn | reltablespace | reldatabase | relfilenode | relblocknumber | forkname | fpi 
+-----+---------------+-------------+-------------+----------------+----------+-----
+     |               |             |             |                |          | 
+(1 row)
+
 -- ===================================================================
 -- Tests for all function executions
 -- ===================================================================
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_record_info(:'wal_lsn1');
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_record_info(:'wal_lsn1');
+ ok 
+----
+ t
+(1 row)
+
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2');
+ ok 
+----
+ t
+(1 row)
+
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1');
  ok 
 ----
  t
 (1 row)
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2');
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', '0/0');
  ok 
 ----
  t
 (1 row)
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info_till_end_of_wal(:'wal_lsn1');
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', NULL);
  ok 
 ----
  t
 (1 row)
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats(:'wal_lsn1', :'wal_lsn2');
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', :'wal_lsn2');
  ok 
 ----
  t
 (1 row)
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats_till_end_of_wal(:'wal_lsn1');
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1');
+ ok 
+----
+ t
+(1 row)
+
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', '0/0');
+ ok 
+----
+ t
+(1 row)
+
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', NULL);
  ok 
 ----
  t
@@ -90,6 +151,14 @@ SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_fpi_info(:'wal_lsn3', :'wal_lsn4')
  t
 (1 row)
 
+-- Check till end of WAL if we get FPI from WAL record.
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_fpi_info(:'wal_lsn3')
+  WHERE relfilenode = :'sample_tbl_oid';
+ ok 
+----
+ t
+(1 row)
+
 -- ===================================================================
 -- Tests for permissions
 -- ===================================================================
diff --git a/contrib/pg_walinspect/meson.build b/contrib/pg_walinspect/meson.build
index bf7b79b1b7..80059f6119 100644
--- a/contrib/pg_walinspect/meson.build
+++ b/contrib/pg_walinspect/meson.build
@@ -30,6 +30,7 @@ tests += {
   'regress': {
     'sql': [
       'pg_walinspect',
+      'oldextversions',
     ],
     # Disabled because these tests require "wal_level=replica", which
     # some runningcheck users do not have (e.g. buildfarm clients).
diff --git a/contrib/pg_walinspect/pg_walinspect--1.0--1.1.sql b/contrib/pg_walinspect/pg_walinspect--1.0--1.1.sql
index 1e9e1e6115..219a088eb9 100644
--- a/contrib/pg_walinspect/pg_walinspect--1.0--1.1.sql
+++ b/contrib/pg_walinspect/pg_walinspect--1.0--1.1.sql
@@ -3,11 +3,64 @@
 -- complain if script is sourced in psql, rather than via ALTER EXTENSION
 \echo Use "ALTER EXTENSION pg_walinspect UPDATE TO '1.1'" to load this file. \quit
 
+/* Drop the unneeded functions and redefine needed ones */
+DROP FUNCTION pg_get_wal_records_info(pg_lsn, pg_lsn);
+DROP FUNCTION pg_get_wal_records_info_till_end_of_wal(pg_lsn);
+DROP FUNCTION pg_get_wal_stats(pg_lsn, pg_lsn, boolean);
+DROP FUNCTION pg_get_wal_stats_till_end_of_wal(pg_lsn, boolean);
+
+--
+-- pg_get_wal_records_info()
+--
+CREATE FUNCTION pg_get_wal_records_info(IN start_lsn pg_lsn,
+    IN end_lsn pg_lsn DEFAULT NULL,
+    OUT start_lsn pg_lsn,
+    OUT end_lsn pg_lsn,
+    OUT prev_lsn pg_lsn,
+    OUT xid xid,
+    OUT resource_manager text,
+    OUT record_type text,
+    OUT record_length int4,
+    OUT main_data_length int4,
+    OUT fpi_length int4,
+    OUT description text,
+    OUT block_ref text
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_get_wal_records_info'
+LANGUAGE C CALLED ON NULL INPUT PARALLEL SAFE;
+
+REVOKE EXECUTE ON FUNCTION pg_get_wal_records_info(pg_lsn, pg_lsn) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_wal_records_info(pg_lsn, pg_lsn) TO pg_read_server_files;
+
+--
+-- pg_get_wal_stats()
+--
+CREATE FUNCTION pg_get_wal_stats(IN start_lsn pg_lsn,
+    IN end_lsn pg_lsn DEFAULT NULL,
+    IN  per_record boolean DEFAULT false,
+    OUT "resource_manager/record_type" text,
+    OUT count int8,
+    OUT count_percentage float8,
+    OUT record_size int8,
+    OUT record_size_percentage float8,
+    OUT fpi_size int8,
+    OUT fpi_size_percentage float8,
+    OUT combined_size int8,
+    OUT combined_size_percentage float8
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_get_wal_stats'
+LANGUAGE C CALLED ON NULL INPUT PARALLEL SAFE;
+
+REVOKE EXECUTE ON FUNCTION pg_get_wal_stats(pg_lsn, pg_lsn, boolean) FROM PUBLIC;
+GRANT EXECUTE ON FUNCTION pg_get_wal_stats(pg_lsn, pg_lsn, boolean) TO pg_read_server_files;
+
 --
 -- pg_get_wal_fpi_info()
 --
 CREATE FUNCTION pg_get_wal_fpi_info(IN start_lsn pg_lsn,
-	IN end_lsn pg_lsn,
+	IN end_lsn pg_lsn DEFAULT NULL,
 	OUT lsn pg_lsn,
 	OUT reltablespace oid,
 	OUT reldatabase oid,
@@ -18,7 +71,7 @@ CREATE FUNCTION pg_get_wal_fpi_info(IN start_lsn pg_lsn,
 )
 RETURNS SETOF record
 AS 'MODULE_PATHNAME', 'pg_get_wal_fpi_info'
-LANGUAGE C STRICT PARALLEL SAFE;
+LANGUAGE C CALLED ON NULL INPUT PARALLEL SAFE;
 
 REVOKE EXECUTE ON FUNCTION pg_get_wal_fpi_info(pg_lsn, pg_lsn) FROM PUBLIC;
 GRANT EXECUTE ON FUNCTION pg_get_wal_fpi_info(pg_lsn, pg_lsn) TO pg_read_server_files;
diff --git a/contrib/pg_walinspect/pg_walinspect.c b/contrib/pg_walinspect/pg_walinspect.c
index b7b0a805ee..4451e3e557 100644
--- a/contrib/pg_walinspect/pg_walinspect.c
+++ b/contrib/pg_walinspect/pg_walinspect.c
@@ -37,13 +37,13 @@ PG_FUNCTION_INFO_V1(pg_get_wal_records_info_till_end_of_wal);
 PG_FUNCTION_INFO_V1(pg_get_wal_stats);
 PG_FUNCTION_INFO_V1(pg_get_wal_stats_till_end_of_wal);
 
-static bool IsFutureLSN(XLogRecPtr lsn, XLogRecPtr *curr_lsn);
+static XLogRecPtr GetCurrentLSN(void);
 static XLogReaderState *InitXLogReaderState(XLogRecPtr lsn);
 static XLogRecord *ReadNextXLogRecord(XLogReaderState *xlogreader);
 static void GetWALRecordInfo(XLogReaderState *record, Datum *values,
 							 bool *nulls, uint32 ncols);
-static XLogRecPtr ValidateInputLSNs(bool till_end_of_wal,
-									XLogRecPtr start_lsn, XLogRecPtr end_lsn);
+static void GetInputLSNs(FunctionCallInfo fcinfo, XLogRecPtr *start_lsn,
+						 XLogRecPtr *end_lsn);
 static void GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
 							  XLogRecPtr end_lsn);
 static void GetXLogSummaryStats(XLogStats *stats, ReturnSetInfo *rsinfo,
@@ -59,27 +59,25 @@ static void GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
 static void GetWALFPIInfo(FunctionCallInfo fcinfo, XLogReaderState *record);
 
 /*
- * Check if the given LSN is in future. Also, return the LSN up to which the
- * server has WAL.
+ * Return the LSN up to which the server has WAL.
  */
-static bool
-IsFutureLSN(XLogRecPtr lsn, XLogRecPtr *curr_lsn)
+static XLogRecPtr
+GetCurrentLSN(void)
 {
+	XLogRecPtr	curr_lsn;
+
 	/*
 	 * We determine the current LSN of the server similar to how page_read
 	 * callback read_local_xlog_page_no_wait does.
 	 */
 	if (!RecoveryInProgress())
-		*curr_lsn = GetFlushRecPtr(NULL);
+		curr_lsn = GetFlushRecPtr(NULL);
 	else
-		*curr_lsn = GetXLogReplayRecPtr(NULL);
+		curr_lsn = GetXLogReplayRecPtr(NULL);
 
-	Assert(!XLogRecPtrIsInvalid(*curr_lsn));
+	Assert(!XLogRecPtrIsInvalid(curr_lsn));
 
-	if (lsn >= *curr_lsn)
-		return true;
-
-	return false;
+	return curr_lsn;
 }
 
 /*
@@ -295,8 +293,14 @@ GetWALFPIInfo(FunctionCallInfo fcinfo, XLogReaderState *record)
  * records between start and end LSNs.  Decompression is applied to the
  * blocks, if necessary.
  *
- * This function emits an error if a future start or end WAL LSN i.e. WAL LSN
- * the database system doesn't know about is specified.
+ * This function determines end LSN if it is not specified or specified as NULL
+ * or invalid. In such cases, the end LSN is assigned with the last flushed LSN
+ * when not in recovery or the last replayed LSN when in recovery.
+ *
+ * This function emits an error if start LSN is invalid or in future i.e. LSN
+ * the database system doesn't know about.
+ *
+ * This function returns NULL, when start LSN is past the end LSN.
  */
 Datum
 pg_get_wal_fpi_info(PG_FUNCTION_ARGS)
@@ -307,10 +311,14 @@ pg_get_wal_fpi_info(PG_FUNCTION_ARGS)
 	MemoryContext old_cxt;
 	MemoryContext tmp_cxt;
 
-	start_lsn = PG_GETARG_LSN(0);
-	end_lsn = PG_GETARG_LSN(1);
+	GetInputLSNs(fcinfo, &start_lsn, &end_lsn);
 
-	end_lsn = ValidateInputLSNs(false, start_lsn, end_lsn);
+	/*
+	 * When start LSN is past the end LSN, let's return NULL instead of raising
+	 * an error for better usability of the functions.
+	 */
+	if (start_lsn >= end_lsn)
+		PG_RETURN_NULL();
 
 	InitMaterializedSRF(fcinfo, 0);
 
@@ -345,8 +353,8 @@ pg_get_wal_fpi_info(PG_FUNCTION_ARGS)
 /*
  * Get WAL record info.
  *
- * This function emits an error if a future WAL LSN i.e. WAL LSN the database
- * system doesn't know about is specified.
+ * This function emits an error if input LSN is invalid or in future i.e. LSN
+ * the database system doesn't know about.
  */
 Datum
 pg_get_wal_record_info(PG_FUNCTION_ARGS)
@@ -363,7 +371,14 @@ pg_get_wal_record_info(PG_FUNCTION_ARGS)
 
 	lsn = PG_GETARG_LSN(0);
 
-	if (IsFutureLSN(lsn, &curr_lsn))
+	if (XLogRecPtrIsInvalid(lsn))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid input LSN")));
+
+	curr_lsn = GetCurrentLSN();
+
+	if (lsn >= curr_lsn)
 	{
 		/*
 		 * GetFlushRecPtr or GetXLogReplayRecPtr gives "end+1" LSN of the last
@@ -402,44 +417,36 @@ pg_get_wal_record_info(PG_FUNCTION_ARGS)
 }
 
 /*
- * Validate the input LSNs and compute end LSN for till_end_of_wal versions.
+ * Get start LSN and get/deduce end LSN.
  */
-static XLogRecPtr
-ValidateInputLSNs(bool till_end_of_wal, XLogRecPtr start_lsn,
-				  XLogRecPtr end_lsn)
+static void
+GetInputLSNs(FunctionCallInfo fcinfo, XLogRecPtr *start_lsn,
+			 XLogRecPtr *end_lsn)
 {
 	XLogRecPtr	curr_lsn;
 
-	if (IsFutureLSN(start_lsn, &curr_lsn))
-	{
-		/*
-		 * GetFlushRecPtr or GetXLogReplayRecPtr gives "end+1" LSN of the last
-		 * record flushed or replayed respectively. But let's use the LSN up
-		 * to "end" in user facing message.
-		 */
+	if (PG_ARGISNULL(0) ||
+		((*start_lsn = PG_GETARG_LSN(0)) == InvalidXLogRecPtr))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot accept future start LSN"),
-				 errdetail("Last known WAL LSN on the database system is at %X/%X.",
-						   LSN_FORMAT_ARGS(curr_lsn))));
-	}
-
-	if (till_end_of_wal)
-		end_lsn = curr_lsn;
+				 errmsg("invalid start LSN")));
 
-	if (end_lsn > curr_lsn)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot accept future end LSN"),
-				 errdetail("Last known WAL LSN on the database system is at %X/%X.",
-						   LSN_FORMAT_ARGS(curr_lsn))));
+	curr_lsn = GetCurrentLSN();
 
-	if (start_lsn >= end_lsn)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("WAL start LSN must be less than end LSN")));
+	if (PG_ARGISNULL(1))
+		*end_lsn = curr_lsn;
+	else
+	{
+		*end_lsn = PG_GETARG_LSN(1);
 
-	return end_lsn;
+		/*
+		 * Adjust end LSN to what the system knows at this point instead of
+		 * raising errors for better usability of the functions.
+		 */
+		if (XLogRecPtrIsInvalid(*end_lsn) ||
+			*end_lsn > curr_lsn)
+			*end_lsn = curr_lsn;
+	}
 }
 
 /*
@@ -494,8 +501,14 @@ GetWALRecordsInfo(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
 /*
  * Get info and data of all WAL records between start LSN and end LSN.
  *
- * This function emits an error if a future start or end WAL LSN i.e. WAL LSN
- * the database system doesn't know about is specified.
+ * This function determines end LSN if it is not specified or specified as NULL
+ * or invalid. In such cases, the end LSN is assigned with the last flushed LSN
+ * when not in recovery or the last replayed LSN when in recovery.
+ *
+ * This function emits an error if start LSN is invalid or in future i.e. LSN
+ * the database system doesn't know about.
+ *
+ * This function returns NULL, when start LSN is past the end LSN.
  */
 Datum
 pg_get_wal_records_info(PG_FUNCTION_ARGS)
@@ -503,31 +516,14 @@ pg_get_wal_records_info(PG_FUNCTION_ARGS)
 	XLogRecPtr	start_lsn;
 	XLogRecPtr	end_lsn;
 
-	start_lsn = PG_GETARG_LSN(0);
-	end_lsn = PG_GETARG_LSN(1);
-
-	end_lsn = ValidateInputLSNs(false, start_lsn, end_lsn);
-
-	GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
-
-	PG_RETURN_VOID();
-}
-
-/*
- * Get info and data of all WAL records from start LSN till end of WAL.
- *
- * This function emits an error if a future start i.e. WAL LSN the database
- * system doesn't know about is specified.
- */
-Datum
-pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
-{
-	XLogRecPtr	start_lsn;
-	XLogRecPtr	end_lsn = InvalidXLogRecPtr;
-
-	start_lsn = PG_GETARG_LSN(0);
+	GetInputLSNs(fcinfo, &start_lsn, &end_lsn);
 
-	end_lsn = ValidateInputLSNs(true, start_lsn, end_lsn);
+	/*
+	 * When start LSN is past the end LSN, let's return NULL instead of raising
+	 * an error for better usability of the functions.
+	 */
+	if (start_lsn >= end_lsn)
+		PG_RETURN_NULL();
 
 	GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
 
@@ -732,8 +728,14 @@ GetWalStats(FunctionCallInfo fcinfo, XLogRecPtr start_lsn,
 /*
  * Get stats of all WAL records between start LSN and end LSN.
  *
- * This function emits an error if a future start or end WAL LSN i.e. WAL LSN
- * the database system doesn't know about is specified.
+ * This function determines end LSN if it is not specified or specified as NULL
+ * or invalid. In such cases, the end LSN is assigned with the last flushed LSN
+ * when not in recovery or the last replayed LSN when in recovery.
+ *
+ * This function emits an error if start LSN is invalid or in future i.e. LSN
+ * the database system doesn't know about.
+ *
+ * This function returns NULL, when start LSN is past the end LSN.
  */
 Datum
 pg_get_wal_stats(PG_FUNCTION_ARGS)
@@ -742,11 +744,16 @@ pg_get_wal_stats(PG_FUNCTION_ARGS)
 	XLogRecPtr	end_lsn;
 	bool		stats_per_record;
 
-	start_lsn = PG_GETARG_LSN(0);
-	end_lsn = PG_GETARG_LSN(1);
-	stats_per_record = PG_GETARG_BOOL(2);
+	GetInputLSNs(fcinfo, &start_lsn, &end_lsn);
+
+	/*
+	 * When start LSN is past the end LSN, let's return NULL instead of raising
+	 * an error for better usability of the functions.
+	 */
+	if (start_lsn >= end_lsn)
+		PG_RETURN_NULL();
 
-	end_lsn = ValidateInputLSNs(false, start_lsn, end_lsn);
+	stats_per_record = PG_GETARG_BOOL(2);
 
 	GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
 
@@ -754,22 +761,70 @@ pg_get_wal_stats(PG_FUNCTION_ARGS)
 }
 
 /*
- * Get stats of all WAL records from start LSN till end of WAL.
+ * NB: Following till_end_of_wal functions have been removed in newer versions
+ * of extension. However, we keep them around for backward compatibility. Which
+ * means, these functions work if someone explicitly installs the older
+ * extension version (using CREATE EXTENSION pg_walinspect WITH VERSION '1.0';)
+ * containing them.
  *
- * This function emits an error if a future start i.e. WAL LSN the database
- * system doesn't know about is specified.
+ * If definitions of these functions are removed completely, the extension
+ * fails to install.
+ *
+ * In newer versions, one can use pg_get_wal_records_info()/pg_get_wal_stats()
+ * for the same till_end_of_wal functionality.
  */
+
+Datum
+pg_get_wal_records_info_till_end_of_wal(PG_FUNCTION_ARGS)
+{
+	XLogRecPtr	start_lsn;
+	XLogRecPtr	end_lsn;
+
+	if (PG_ARGISNULL(0) ||
+		((start_lsn = PG_GETARG_LSN(0)) == InvalidXLogRecPtr))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid start LSN")));
+
+	/* Let's compute end LSN ourselves. */
+	end_lsn = GetCurrentLSN();
+
+	/*
+	 * When start LSN is past the end LSN, let's return NULL instead of raising
+	 * an error for better usability of the functions.
+	 */
+	if (start_lsn >= end_lsn)
+		PG_RETURN_NULL();
+
+	GetWALRecordsInfo(fcinfo, start_lsn, end_lsn);
+
+	PG_RETURN_VOID();
+}
+
 Datum
 pg_get_wal_stats_till_end_of_wal(PG_FUNCTION_ARGS)
 {
 	XLogRecPtr	start_lsn;
-	XLogRecPtr	end_lsn = InvalidXLogRecPtr;
+	XLogRecPtr	end_lsn;
 	bool		stats_per_record;
 
-	start_lsn = PG_GETARG_LSN(0);
-	stats_per_record = PG_GETARG_BOOL(1);
+	if (PG_ARGISNULL(0) ||
+		((start_lsn = PG_GETARG_LSN(0)) == InvalidXLogRecPtr))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid start LSN")));
+
+	/* Let's compute end LSN ourselves. */
+	end_lsn = GetCurrentLSN();
+
+	/*
+	 * When start LSN is past the end LSN, let's return NULL instead of raising
+	 * an error for better usability of the functions.
+	 */
+	if (start_lsn >= end_lsn)
+		PG_RETURN_NULL();
 
-	end_lsn = ValidateInputLSNs(true, start_lsn, end_lsn);
+	stats_per_record = PG_GETARG_BOOL(2);
 
 	GetWalStats(fcinfo, start_lsn, end_lsn, stats_per_record);
 
diff --git a/contrib/pg_walinspect/sql/oldextversions.sql b/contrib/pg_walinspect/sql/oldextversions.sql
new file mode 100644
index 0000000000..2c4f484177
--- /dev/null
+++ b/contrib/pg_walinspect/sql/oldextversions.sql
@@ -0,0 +1,27 @@
+-- test old extension version entry points
+
+DROP EXTENSION pg_walinspect;
+CREATE EXTENSION pg_walinspect WITH VERSION '1.0';
+
+-- Move to new version 1.1
+ALTER EXTENSION pg_walinspect UPDATE TO '1.1';
+
+-- New function introduced in 1.1
+SELECT pg_get_functiondef('pg_get_wal_fpi_info'::regproc);
+
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_records_info'::regproc);
+
+-- Removed function
+SELECT pg_get_functiondef('pg_get_wal_records_info_till_end_of_wal'::regproc);
+
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_stats'::regproc);
+
+-- Removed function
+SELECT pg_get_functiondef('pg_get_wal_stats_till_end_of_wal'::regproc);
+
+-- Redefined function
+SELECT pg_get_functiondef('pg_get_wal_fpi_info'::regproc);
+
+DROP EXTENSION pg_walinspect;
diff --git a/contrib/pg_walinspect/sql/pg_walinspect.sql b/contrib/pg_walinspect/sql/pg_walinspect.sql
index 849201a1f8..1d73782388 100644
--- a/contrib/pg_walinspect/sql/pg_walinspect.sql
+++ b/contrib/pg_walinspect/sql/pg_walinspect.sql
@@ -17,23 +17,56 @@ INSERT INTO sample_tbl SELECT * FROM generate_series(3, 4);
 -- Tests for input validation
 -- ===================================================================
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info(:'wal_lsn2', :'wal_lsn1'); -- ERROR
+-- Invalid input LSNs
+SELECT * FROM pg_get_wal_record_info('0/0'); -- ERROR
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats(:'wal_lsn2', :'wal_lsn1'); -- ERROR
+SELECT * FROM pg_get_wal_records_info('0/0'); -- ERROR
+
+SELECT * FROM pg_get_wal_stats('0/0'); -- ERROR
+
+SELECT * FROM pg_get_wal_fpi_info('0/0'); -- ERROR
+
+-- Start LSN NULL
+SELECT * FROM pg_get_wal_records_info(NULL); -- ERROR
+
+SELECT * FROM pg_get_wal_stats(NULL); -- ERROR
+
+SELECT * FROM pg_get_wal_fpi_info(NULL); -- ERROR
+
+-- Start LSN > End LSN
+SELECT * FROM pg_get_wal_records_info(:'wal_lsn2', :'wal_lsn1'); -- NULL
+
+SELECT * FROM pg_get_wal_stats(:'wal_lsn2', :'wal_lsn1'); -- NULL
+
+SELECT * FROM pg_get_wal_fpi_info(:'wal_lsn2', :'wal_lsn1'); -- NULL
 
 -- ===================================================================
 -- Tests for all function executions
 -- ===================================================================
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_record_info(:'wal_lsn1');
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_record_info(:'wal_lsn1');
+
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2');
+
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1');
+
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', '0/0');
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', :'wal_lsn2');
+-- Get info till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_records_info(:'wal_lsn1', NULL);
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_records_info_till_end_of_wal(:'wal_lsn1');
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', :'wal_lsn2');
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats(:'wal_lsn1', :'wal_lsn2');
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1');
 
-SELECT COUNT(*) >= 0 AS ok FROM pg_get_wal_stats_till_end_of_wal(:'wal_lsn1');
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', '0/0');
+
+-- Get stats till end of WAL
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_stats(:'wal_lsn1', NULL);
 
 -- ===================================================================
 -- Test for filtering out WAL records of a particular table
@@ -68,6 +101,10 @@ SELECT pg_current_wal_lsn() AS wal_lsn4 \gset
 SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_fpi_info(:'wal_lsn3', :'wal_lsn4')
   WHERE relfilenode = :'sample_tbl_oid';
 
+-- Check till end of WAL if we get FPI from WAL record.
+SELECT COUNT(*) >= 1 AS ok FROM pg_get_wal_fpi_info(:'wal_lsn3')
+  WHERE relfilenode = :'sample_tbl_oid';
+
 -- ===================================================================
 -- Tests for permissions
 -- ===================================================================
diff --git a/doc/src/sgml/pgwalinspect.sgml b/doc/src/sgml/pgwalinspect.sgml
index 3d7cdb95cc..86ce4381d5 100644
--- a/doc/src/sgml/pgwalinspect.sgml
+++ b/doc/src/sgml/pgwalinspect.sgml
@@ -61,6 +61,7 @@
       Gets WAL record information of a given LSN. If the given LSN isn't
       at the start of a WAL record, it gives the information of the next
       available valid WAL record; or an error if no such record is found.
+      If given LSN is not yet available, the function will raise an error.
       For example, usage of the function is as
       follows:
 <screen>
@@ -85,7 +86,7 @@ block_ref        | blkref #0: rel 1663/5/60221 fork main blk 2
     <varlistentry id="pgwalinspect-funcs-pg-get-wal-records-info">
     <term>
      <function>
-      pg_get_wal_records_info(start_lsn pg_lsn, end_lsn pg_lsn)
+      pg_get_wal_records_info(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL)
       returns setof record
      </function>
     </term>
@@ -94,8 +95,9 @@ block_ref        | blkref #0: rel 1663/5/60221 fork main blk 2
      <para>
       Gets information of all the valid WAL records between
       <replaceable>start_lsn</replaceable> and <replaceable>end_lsn</replaceable>.
-      Returns one row per WAL record. If <replaceable>start_lsn</replaceable>
-      or <replaceable>end_lsn</replaceable> are not yet available, the
+      Returns one row per WAL record. If <replaceable>end_lsn</replaceable>
+      isn't specified, it returns information till the end of WAL.
+      If <replaceable>start_lsn</replaceable> is not yet available, the
       function will raise an error. For example:
 <screen>
 postgres=# SELECT * FROM pg_get_wal_records_info('0/1E913618', '0/1E913740') LIMIT 1;
@@ -116,27 +118,10 @@ block_ref        |
     </listitem>
    </varlistentry>
 
-    <varlistentry id="pgwalinspect-funcs-pg-get-wal-records-info-till-end-of-wal">
-    <term>
-     <function>
-      pg_get_wal_records_info_till_end_of_wal(start_lsn pg_lsn)
-      returns setof record
-     </function>
-    </term>
-
-    <listitem>
-     <para>
-      This function is the same as <function>pg_get_wal_records_info()</function>,
-      except that it gets information of all the valid WAL records from
-      <replaceable>start_lsn</replaceable> till the end of WAL.
-     </para>
-    </listitem>
-   </varlistentry>
-
     <varlistentry id="pgwalinspect-funcs-pg-get-wal-stats">
     <term>
      <function>
-      pg_get_wal_stats(start_lsn pg_lsn, end_lsn pg_lsn, per_record boolean DEFAULT false)
+      pg_get_wal_stats(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL, per_record boolean DEFAULT false)
       returns setof record
      </function>
     </term>
@@ -149,9 +134,9 @@ block_ref        |
       <replaceable>resource_manager</replaceable> type. When
       <replaceable>per_record</replaceable> is set to <literal>true</literal>,
       it returns one row per <replaceable>record_type</replaceable>.
-      If <replaceable>start_lsn</replaceable>
-      or <replaceable>end_lsn</replaceable> are not yet available, the
-      function will raise an error. For example:
+      If <replaceable>end_lsn</replaceable> isn't specified, it returns
+      information till the end of WAL. If <replaceable>start_lsn</replaceable>
+      is not yet available, the function will raise an error. For example:
 <screen>
 postgres=# SELECT * FROM pg_get_wal_stats('0/1E847D00', '0/1E84F500')
              WHERE count > 0 LIMIT 1 AND
@@ -171,28 +156,14 @@ combined_size_percentage     | 2.8634072910530795
     </listitem>
    </varlistentry>
 
-    <varlistentry id="pgwalinspect-funcs-pg-get-wal-stats-till-end-of-wal">
+   <varlistentry>
     <term>
      <function>
-      pg_get_wal_stats_till_end_of_wal(start_lsn pg_lsn, per_record boolean DEFAULT false)
+      pg_get_wal_fpi_info(start_lsn pg_lsn, end_lsn pg_lsn DEFAULT NULL)
       returns setof record
      </function>
     </term>
 
-    <listitem>
-     <para>
-      This function is the same as <function>pg_get_wal_stats()</function>,
-      except that it gets statistics of all the valid WAL records from
-      <replaceable>start_lsn</replaceable> till end of WAL.
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term>
-     <function>pg_get_wal_fpi_info(start_lsn pg_lsn, end_lsn pg_lsn) returns setof record</function>
-    </term>
-
     <listitem>
      <para>
       Gets a copy of full page images as <type>bytea</type> values (after
@@ -200,9 +171,9 @@ combined_size_percentage     | 2.8634072910530795
       with all the valid WAL records between
       <replaceable>start_lsn</replaceable> and
       <replaceable>end_lsn</replaceable>. Returns one row per full page image.
-      If <replaceable>start_lsn</replaceable> or
-      <replaceable>end_lsn</replaceable> are not yet available, the function
-      will raise an error. For example:
+      If <replaceable>end_lsn</replaceable> isn't specified, it returns
+      information till the end of WAL. If <replaceable>start_lsn</replaceable>
+      is not yet available, the function will raise an error. For example:
 <screen>
 postgres=# SELECT lsn, reltablespace, reldatabase, relfilenode, relblocknumber,
                   forkname, substring(fpi for 24) as fpi_trimmed
@@ -219,8 +190,20 @@ fpi_trimmed    | \x00000000b89e660100000000a003c0030020042000000000
      </para>
     </listitem>
    </varlistentry>
-
   </variablelist>
+
+ <note>
+  <para>
+   Note that <function>pg_get_wal_records_info_till_end_of_wal</function> and
+   <function>pg_get_wal_stats_till_end_of_wal</function> functions have been
+   removed in the <filename>pg_walinspect</filename> version
+   <literal>1.1</literal>. The same functionality can be achieved with
+   <function>pg_get_wal_records_info</function> and
+   <function>pg_get_wal_stats</function> functions. However,
+   <function>till_end_of_wal</function> functions will still work if the
+   extension is installed explicitly with version <literal>1.0</literal>.
+  </para>
+ </note>
  </sect2>
 
  <sect2 id="pgwalinspect-author">
-- 
2.34.1

