From 1ed547640b7fe97f27032d7da6f50d2ba28db9c3 Mon Sep 17 00:00:00 2001
From: Shinya Kato <shinya11.kato@gmail.com>
Date: Fri, 2 May 2025 18:13:45 +0900
Subject: [PATCH v2 7/9] Make pg_stat_reset_replication_slot() return the reset
 time

---
 contrib/test_decoding/expected/stats.out     | 35 +++++++++++++++-----
 contrib/test_decoding/sql/stats.sql          |  9 +++--
 doc/src/sgml/monitoring.sgml                 |  9 ++---
 src/backend/utils/activity/pgstat_replslot.c |  9 +++--
 src/backend/utils/adt/pgstatfuncs.c          | 10 ++++--
 src/include/catalog/pg_proc.dat              |  2 +-
 src/include/pgstat.h                         |  2 +-
 7 files changed, 55 insertions(+), 21 deletions(-)

diff --git a/contrib/test_decoding/expected/stats.out b/contrib/test_decoding/expected/stats.out
index de6dc416130..874a52eb28e 100644
--- a/contrib/test_decoding/expected/stats.out
+++ b/contrib/test_decoding/expected/stats.out
@@ -47,10 +47,10 @@ SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count,
 
 RESET logical_decoding_work_mem;
 -- reset stats for one slot, others should be unaffected
-SELECT pg_stat_reset_replication_slot('regression_slot_stats1');
- pg_stat_reset_replication_slot 
---------------------------------
- 
+SELECT pg_stat_reset_replication_slot('regression_slot_stats1') IS NOT NULL AS t;
+ t 
+---
+ t
 (1 row)
 
 SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
@@ -62,10 +62,10 @@ SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count,
 (3 rows)
 
 -- reset stats for all slots
-SELECT pg_stat_reset_replication_slot(NULL);
- pg_stat_reset_replication_slot 
---------------------------------
- 
+SELECT pg_stat_reset_replication_slot(NULL) IS NOT NULL AS t;
+ t 
+---
+ t
 (1 row)
 
 SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
@@ -91,6 +91,25 @@ SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
  do-not-exist |          0 |           0 |           0 |           0 |            0 |            0 |          0 |           0 | 
 (1 row)
 
+-- verify physical replication slot stats are not reset
+SELECT 'init' FROM pg_create_physical_replication_slot('regression_slot_stats_physical') ps;
+ ?column? 
+----------
+ init
+(1 row)
+
+SELECT pg_stat_reset_replication_slot('regression_slot_stats_physical');
+ pg_stat_reset_replication_slot 
+--------------------------------
+ 
+(1 row)
+
+SELECT pg_drop_replication_slot('regression_slot_stats_physical');
+ pg_drop_replication_slot 
+--------------------------
+ 
+(1 row)
+
 -- spilling the xact
 BEGIN;
 INSERT INTO stats_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i);
diff --git a/contrib/test_decoding/sql/stats.sql b/contrib/test_decoding/sql/stats.sql
index a022fe1bf07..8ca60f788e3 100644
--- a/contrib/test_decoding/sql/stats.sql
+++ b/contrib/test_decoding/sql/stats.sql
@@ -19,11 +19,11 @@ SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count,
 RESET logical_decoding_work_mem;
 
 -- reset stats for one slot, others should be unaffected
-SELECT pg_stat_reset_replication_slot('regression_slot_stats1');
+SELECT pg_stat_reset_replication_slot('regression_slot_stats1') IS NOT NULL AS t;
 SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
 
 -- reset stats for all slots
-SELECT pg_stat_reset_replication_slot(NULL);
+SELECT pg_stat_reset_replication_slot(NULL) IS NOT NULL AS t;
 SELECT slot_name, spill_txns = 0 AS spill_txns, spill_count = 0 AS spill_count, total_txns > 0 AS total_txns, total_bytes > 0 AS total_bytes FROM pg_stat_replication_slots ORDER BY slot_name;
 
 -- verify accessing/resetting stats for non-existent slot does something reasonable
@@ -31,6 +31,11 @@ SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
 SELECT pg_stat_reset_replication_slot('do-not-exist');
 SELECT * FROM pg_stat_get_replication_slot('do-not-exist');
 
+-- verify physical replication slot stats are not reset
+SELECT 'init' FROM pg_create_physical_replication_slot('regression_slot_stats_physical') ps;
+SELECT pg_stat_reset_replication_slot('regression_slot_stats_physical');
+SELECT pg_drop_replication_slot('regression_slot_stats_physical');
+
 -- spilling the xact
 BEGIN;
 INSERT INTO stats_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i);
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index df90f4668c4..27f22e6fd76 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -5183,12 +5183,13 @@ description | Waiting for a newly initialized WAL file to reach durable storage
           <primary>pg_stat_reset_replication_slot</primary>
         </indexterm>
         <function>pg_stat_reset_replication_slot</function> ( <type>text</type> )
-        <returnvalue>void</returnvalue>
+        <returnvalue>timestamp with time zone</returnvalue>
        </para>
        <para>
-        Resets statistics of the replication slot defined by the argument. If
-        the argument is <literal>NULL</literal>, resets statistics for all
-        the replication slots.
+        Resets statistics of the replication slot defined by the argument, and
+        returns the time of the reset or <literal>NULL</literal> if the replication
+        slot defined by the argument is a physical slot. If the argument is
+        <literal>NULL</literal>, resets statistics for all the replication slots.
        </para>
        <para>
          This function is restricted to superusers by default, but other users
diff --git a/src/backend/utils/activity/pgstat_replslot.c b/src/backend/utils/activity/pgstat_replslot.c
index ccfb11c49bf..67f5c53989e 100644
--- a/src/backend/utils/activity/pgstat_replslot.c
+++ b/src/backend/utils/activity/pgstat_replslot.c
@@ -37,11 +37,14 @@ static int	get_replslot_index(const char *name, bool need_lock);
  *
  * Permission checking for this function is managed through the normal
  * GRANT system.
+ *
+ * NB: Returns the time of the reset or 0 if it is a physical slot.
  */
-void
+TimestampTz
 pgstat_reset_replslot(const char *name)
 {
 	ReplicationSlot *slot;
+	TimestampTz ts = 0;
 
 	Assert(name != NULL);
 
@@ -61,10 +64,12 @@ pgstat_reset_replslot(const char *name)
 	 * as we collect stats only for logical slots.
 	 */
 	if (SlotIsLogical(slot))
-		pgstat_reset(PGSTAT_KIND_REPLSLOT, InvalidOid,
+		ts = pgstat_reset(PGSTAT_KIND_REPLSLOT, InvalidOid,
 					 ReplicationSlotIndex(slot));
 
 	LWLockRelease(ReplicationSlotControlLock);
+
+	return ts;
 }
 
 /*
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index b28ceab91d9..7d9df708266 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1999,16 +1999,20 @@ Datum
 pg_stat_reset_replication_slot(PG_FUNCTION_ARGS)
 {
 	char	   *target = NULL;
+	TimestampTz ts;
 
 	if (PG_ARGISNULL(0))
-		pgstat_reset_of_kind(PGSTAT_KIND_REPLSLOT);
+		ts = pgstat_reset_of_kind(PGSTAT_KIND_REPLSLOT);
 	else
 	{
 		target = text_to_cstring(PG_GETARG_TEXT_PP(0));
-		pgstat_reset_replslot(target);
+		ts = pgstat_reset_replslot(target);
 	}
 
-	PG_RETURN_VOID();
+	if (ts == 0)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TIMESTAMPTZ(ts);
 }
 
 /* Reset subscription stats (a specific one or all of them) */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 87b14848c22..757c4711a85 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6180,7 +6180,7 @@
 { oid => '6170',
   descr => 'statistics: reset collected statistics for a single replication slot',
   proname => 'pg_stat_reset_replication_slot', proisstrict => 'f',
-  provolatile => 'v', prorettype => 'void', proargtypes => 'text',
+  provolatile => 'v', prorettype => 'timestamptz', proargtypes => 'text',
   prosrc => 'pg_stat_reset_replication_slot' },
 { oid => '6232',
   descr => 'statistics: reset collected statistics for a single subscription',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 2d741980ef5..d2d9479fc0f 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -733,7 +733,7 @@ extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
  * Functions in pgstat_replslot.c
  */
 
-extern void pgstat_reset_replslot(const char *name);
+extern TimestampTz pgstat_reset_replslot(const char *name);
 struct ReplicationSlot;
 extern void pgstat_report_replslot(struct ReplicationSlot *slot, const PgStat_StatReplSlotEntry *repSlotStat);
 extern void pgstat_create_replslot(struct ReplicationSlot *slot);
-- 
2.47.3

