From e2fab62a747ecc37a93f644be2c00e40891fc6bb Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Date: Fri, 8 Aug 2025 04:40:18 +0000
Subject: [PATCH v1 06/10] Adding idx_tup_read to pg_stat_backend

Adding per backend number of index entries returned by index scans.

XXX: Bump catversion.
---
 doc/src/sgml/monitoring.sgml                |  9 +++++++++
 src/backend/access/index/indexam.c          |  2 ++
 src/backend/catalog/system_views.sql        |  1 +
 src/backend/utils/activity/pgstat_backend.c | 12 ++++++++++++
 src/backend/utils/adt/pgstatfuncs.c         |  3 ++-
 src/include/catalog/pg_proc.dat             |  6 +++---
 src/include/pgstat.h                        |  3 +++
 src/test/regress/expected/rules.out         |  3 ++-
 8 files changed, 34 insertions(+), 5 deletions(-)
  19.9% doc/src/sgml/
   6.6% src/backend/access/index/
  20.3% src/backend/utils/activity/
  10.4% src/backend/utils/adt/
  19.9% src/include/catalog/
   9.4% src/include/
  11.1% src/test/regress/expected/

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 6aa467b1e7d..a6313a32299 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1258,6 +1258,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>idx_tup_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       The number of index entries returned by index scans.
+      </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/access/index/indexam.c b/src/backend/access/index/indexam.c
index d7d06266e70..7252841cedb 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -652,6 +652,7 @@ index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
 	Assert(ItemPointerIsValid(&scan->xs_heaptid));
 
 	pgstat_count_index_tuples(scan->indexRelation, 1);
+	pgstat_count_backend_rel_idx_tup_read(1);
 
 	/* Return the TID of the tuple we found. */
 	return &scan->xs_heaptid;
@@ -781,6 +782,7 @@ index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
 	ntids = scan->indexRelation->rd_indam->amgetbitmap(scan, bitmap);
 
 	pgstat_count_index_tuples(scan->indexRelation, ntids);
+	pgstat_count_backend_rel_idx_tup_read(ntids);
 
 	return ntids;
 }
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7193ab693fb..d87fd38b8b2 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -918,6 +918,7 @@ CREATE VIEW pg_stat_backend AS
             S.seq_tup_read,
             S.idx_tup_fetch,
             S.idx_scan,
+            S.idx_tup_read,
             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 dc501306901..df072cb082f 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -290,6 +290,7 @@ pgstat_flush_backend_entry_rel(PgStat_EntryRef *entry_ref)
 	BACKENDREL_ACC(seq_tup_read);
 	BACKENDREL_ACC(idx_tup_fetch);
 	BACKENDREL_ACC(idx_scan);
+	BACKENDREL_ACC(idx_tup_read);
 #undef BACKENDREL_ACC
 
 	/*
@@ -471,3 +472,14 @@ PGSTAT_COUNT_BACKEND_FUNC(idx_tup_fetch)
 
 /* pgstat_count_backend_rel_idx_scan */
 PGSTAT_COUNT_BACKEND_FUNC(idx_scan)
+
+void
+pgstat_count_backend_rel_idx_tup_read(PgStat_Counter n)
+{
+	if (!pgstat_tracks_backend_bktype(MyBackendType))
+		return;
+
+	PendingBackendStats.pending_backendrel.idx_tup_read += n;
+	backend_has_relstats = true;
+	pgstat_report_fixed = true;
+}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 60298c5f8d9..99486851281 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	6
+#define PG_STAT_GET_BACKEND_STATS_COLS	7
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -729,6 +729,7 @@ pg_stat_get_backend_statistics(PG_FUNCTION_ARGS)
 		values[i++] = Int64GetDatum(backend_stats->seq_tup_read);
 		values[i++] = Int64GetDatum(backend_stats->idx_tup_fetch);
 		values[i++] = Int64GetDatum(backend_stats->idx_scan);
+		values[i++] = Int64GetDatum(backend_stats->idx_tup_read);
 
 		if (backend_stats->stat_reset_timestamp != 0)
 			values[i] = TimestampTzGetDatum(backend_stats->stat_reset_timestamp);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9212faa0506..bb5608f492e 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,timestamptz}',
-  proargmodes => '{i,o,o,o,o,o,o}',
-  proargnames => '{pid,pid,seq_scan,seq_tup_read,idx_tup_fetch,idx_scan,stats_reset}',
+  proallargtypes => '{int4,int4,int8,int8,int8,int8,int8,timestamptz}',
+  proargmodes => '{i,o,o,o,o,o,o,o}',
+  proargnames => '{pid,pid,seq_scan,seq_tup_read,idx_tup_fetch,idx_scan,idx_tup_read,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 a55811ef8c3..f26b9e12567 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -494,6 +494,7 @@ typedef struct PgStat_Backend
 	PgStat_Counter seq_tup_read;
 	PgStat_Counter idx_tup_fetch;
 	PgStat_Counter idx_scan;
+	PgStat_Counter idx_tup_read;
 } PgStat_Backend;
 
 typedef struct PgStat_BackendRelPending
@@ -502,6 +503,7 @@ typedef struct PgStat_BackendRelPending
 	PgStat_Counter seq_tup_read;
 	PgStat_Counter idx_tup_fetch;
 	PgStat_Counter idx_scan;
+	PgStat_Counter idx_tup_read;
 } PgStat_BackendRelPending;
 
 /* ---------
@@ -585,6 +587,7 @@ extern void pgstat_count_backend_rel_heap_scan(void);
 extern void pgstat_count_backend_rel_seq_tup_read(void);
 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);
 
 /*
  * Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 007631d2421..2da8a8122f1 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1852,8 +1852,9 @@ pg_stat_backend| SELECT pid,
     seq_tup_read,
     idx_tup_fetch,
     idx_scan,
+    idx_tup_read,
     stats_reset
-   FROM pg_stat_get_backend_statistics(NULL::integer) s(pid, seq_scan, seq_tup_read, idx_tup_fetch, idx_scan, 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, 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

