From cdfd76728e8297f0b460cc37a2c8bb6c2adf5b72 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Date: Fri, 8 Aug 2025 07:42:53 +0000
Subject: [PATCH v1 08/10] Adding last_vacuum to pg_stat_backend

Adding the last time that a backend triggered a manual vacuum.

XXX: Bump catversion.
---
 doc/src/sgml/monitoring.sgml                 |  9 ++++++++
 src/backend/catalog/system_views.sql         |  1 +
 src/backend/utils/activity/pgstat_backend.c  | 23 +++++++++++++++++++-
 src/backend/utils/activity/pgstat_relation.c |  1 +
 src/backend/utils/adt/pgstatfuncs.c          |  7 +++++-
 src/include/catalog/pg_proc.dat              |  6 ++---
 src/include/pgstat.h                         |  3 +++
 src/test/regress/expected/rules.out          |  3 ++-
 8 files changed, 47 insertions(+), 6 deletions(-)
  18.6% doc/src/sgml/
  42.6% src/backend/utils/activity/
  12.5% src/backend/utils/adt/
  13.3% src/include/catalog/
   6.7% src/include/
   4.5% src/test/regress/expected/

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 1b3154d5326..d27369a4d21 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1276,6 +1276,15 @@ description | Waiting for a newly initialized WAL file to reach durable storage
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>last_vacuum</structfield> <type>timestamp with time zone</type>
+      </para>
+      <para>
+       Last time at which this backend triggered a manual vacuum (not counting VACUUM FULL).
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
         <structfield>stats_reset</structfield> <type>timestamp with time zone</type>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 6257bce86a1..5450d60a084 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -920,6 +920,7 @@ CREATE VIEW pg_stat_backend AS
             S.idx_scan,
             S.idx_tup_read,
             S.vacuum_count,
+            S.last_vacuum,
             S.stats_reset
     FROM pg_stat_get_backend_statistics(NULL) AS S;
 
diff --git a/src/backend/utils/activity/pgstat_backend.c b/src/backend/utils/activity/pgstat_backend.c
index 58386662359..c248da52bc2 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -292,7 +292,14 @@ pgstat_flush_backend_entry_rel(PgStat_EntryRef *entry_ref)
 	BACKENDREL_ACC(idx_scan);
 	BACKENDREL_ACC(idx_tup_read);
 	BACKENDREL_ACC(vacuum_count);
-#undef BACKENDREL_ACC
+#undef BACKENDREL_AC
+
+#define BACKENDREL_SET(stat) \
+	if (PendingBackendStats.pending_backendrel.stat > 0) \
+		(shbackendent->stats.stat = PendingBackendStats.pending_backendrel.stat)
+
+	BACKENDREL_SET(last_vacuum);
+#undef BACKENDREL_SET
 
 	/*
 	 * Clear out the statistics buffer, so it can be re-used.
@@ -487,3 +494,17 @@ pgstat_count_backend_rel_idx_tup_read(PgStat_Counter n)
 	backend_has_relstats = true;
 	pgstat_report_fixed = true;
 }
+
+#define PGSTAT_SETTS_BACKEND_FUNC(stat)						\
+void														\
+CppConcat(pgstat_set_backend_rel_,stat)(TimestampTz ts)		\
+{															\
+	if (!pgstat_tracks_backend_bktype(MyBackendType))		\
+		return;												\
+	PendingBackendStats.pending_backendrel.stat= ts;		\
+	backend_has_relstats = true;							\
+	pgstat_report_fixed = true;								\
+}
+
+/* pgstat_set_backend_rel_last_vacuum */
+PGSTAT_SETTS_BACKEND_FUNC(last_vacuum)
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 555f0815454..d22ee2224a7 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -259,6 +259,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
 		tabentry->vacuum_count++;
 		tabentry->total_vacuum_time += elapsedtime;
 		pgstat_count_backend_rel_vacuum_count();
+		pgstat_set_backend_rel_last_vacuum(ts);
 	}
 
 	pgstat_unlock_entry(entry_ref);
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index bff5bfc0538..8abf43185e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -691,7 +691,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_backend_statistics(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_BACKEND_STATS_COLS	8
+#define PG_STAT_GET_BACKEND_STATS_COLS	9
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -732,6 +732,11 @@ pg_stat_get_backend_statistics(PG_FUNCTION_ARGS)
 		values[i++] = Int64GetDatum(backend_stats->idx_tup_read);
 		values[i++] = Int64GetDatum(backend_stats->vacuum_count);
 
+		if (backend_stats->last_vacuum != 0)
+			values[i++] = TimestampTzGetDatum(backend_stats->last_vacuum);
+		else
+			nulls[i++] = true;
+
 		if (backend_stats->stat_reset_timestamp != 0)
 			values[i] = TimestampTzGetDatum(backend_stats->stat_reset_timestamp);
 		else
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index aae43548923..4c73c91bf84 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5646,9 +5646,9 @@
   proname => 'pg_stat_get_backend_statistics', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,int4,int8,int8,int8,int8,int8,int8,timestamptz}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,pid,seq_scan,seq_tup_read,idx_tup_fetch,idx_scan,idx_tup_read,vacuum_count,stats_reset}',
+  proallargtypes => '{int4,int4,int8,int8,int8,int8,int8,int8,timestamptz,timestamptz}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,pid,seq_scan,seq_tup_read,idx_tup_fetch,idx_scan,idx_tup_read,vacuum_count,last_vacuum,stats_reset}',
   prosrc => 'pg_stat_get_backend_statistics' },
 { oid => '6318', descr => 'describe wait events',
   proname => 'pg_get_wait_events', procost => '10', prorows => '250',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 558bfd3c123..5c1c49aaed0 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -496,6 +496,7 @@ typedef struct PgStat_Backend
 	PgStat_Counter idx_scan;
 	PgStat_Counter idx_tup_read;
 	PgStat_Counter vacuum_count;
+	TimestampTz last_vacuum;
 } PgStat_Backend;
 
 typedef struct PgStat_BackendRelPending
@@ -506,6 +507,7 @@ typedef struct PgStat_BackendRelPending
 	PgStat_Counter idx_scan;
 	PgStat_Counter idx_tup_read;
 	PgStat_Counter vacuum_count;
+	TimestampTz last_vacuum;
 } PgStat_BackendRelPending;
 
 /* ---------
@@ -591,6 +593,7 @@ extern void pgstat_count_backend_rel_idx_tup_fetch(void);
 extern void pgstat_count_backend_rel_idx_scan(void);
 extern void pgstat_count_backend_rel_idx_tup_read(PgStat_Counter n);
 extern void pgstat_count_backend_rel_vacuum_count(void);
+extern void pgstat_set_backend_rel_last_vacuum(TimestampTz ts);
 
 /*
  * Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7df7fe37ed4..89f03c7097c 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1854,8 +1854,9 @@ pg_stat_backend| SELECT pid,
     idx_scan,
     idx_tup_read,
     vacuum_count,
+    last_vacuum,
     stats_reset
-   FROM pg_stat_get_backend_statistics(NULL::integer) s(pid, seq_scan, seq_tup_read, idx_tup_fetch, idx_scan, idx_tup_read, vacuum_count, stats_reset);
+   FROM pg_stat_get_backend_statistics(NULL::integer) s(pid, seq_scan, seq_tup_read, idx_tup_fetch, idx_scan, idx_tup_read, vacuum_count, last_vacuum, stats_reset);
 pg_stat_bgwriter| SELECT pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean,
     pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean,
     pg_stat_get_buf_alloc() AS buffers_alloc,
-- 
2.34.1

