From 10181916f61cac7be926e9ca37623513e35b3d84 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Date: Fri, 8 Aug 2025 03:17:59 +0000
Subject: [PATCH v1 05/10] Adding idx_scan to pg_stat_backend

Adding per backend number of index scans initiated.

XXX: Bump catversion.
---
 doc/src/sgml/monitoring.sgml                |  9 +++++++++
 src/backend/access/brin/brin.c              |  1 +
 src/backend/access/gin/ginscan.c            |  1 +
 src/backend/access/gist/gistget.c           |  2 ++
 src/backend/access/hash/hashsearch.c        |  1 +
 src/backend/access/nbtree/nbtsearch.c       |  1 +
 src/backend/access/spgist/spgscan.c         |  1 +
 src/backend/catalog/system_views.sql        |  1 +
 src/backend/utils/activity/pgstat_backend.c |  4 ++++
 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 ++-
 src/test/regress/expected/stats.out         | 15 +++++++++------
 src/test/regress/sql/stats.sql              |  9 ++++++---
 15 files changed, 46 insertions(+), 14 deletions(-)
  10.5% doc/src/sgml/
   3.2% src/backend/access/gist/
   7.9% src/backend/access/
   4.3% src/backend/utils/activity/
   5.7% src/backend/utils/adt/
  15.2% src/include/catalog/
   4.3% src/include/
  30.8% src/test/regress/expected/
  16.6% src/test/regress/sql/

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 2bfd016a2ff..6aa467b1e7d 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1249,6 +1249,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_scan</structfield> <type>bigint</type>
+      </para>
+      <para>
+       The number of index scans initiated.
+      </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/brin/brin.c b/src/backend/access/brin/brin.c
index 7ff7467e462..c9d0f2eff9a 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -592,6 +592,7 @@ bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 	opaque = (BrinOpaque *) scan->opaque;
 	bdesc = opaque->bo_bdesc;
 	pgstat_count_index_scan(idxRel);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index c2d1771bd77..26efd4ceced 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -442,6 +442,7 @@ ginNewScanKey(IndexScanDesc scan)
 	MemoryContextSwitchTo(oldCtx);
 
 	pgstat_count_index_scan(scan->indexRelation);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 }
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index 387d9972345..19517a4cdb2 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -625,6 +625,7 @@ gistgettuple(IndexScanDesc scan, ScanDirection dir)
 		GISTSearchItem fakeItem;
 
 		pgstat_count_index_scan(scan->indexRelation);
+		pgstat_count_backend_rel_idx_scan();
 		if (scan->instrument)
 			scan->instrument->nsearches++;
 
@@ -752,6 +753,7 @@ gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
 		return 0;
 
 	pgstat_count_index_scan(scan->indexRelation);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 
diff --git a/src/backend/access/hash/hashsearch.c b/src/backend/access/hash/hashsearch.c
index 92c15a65be2..2350cf5190a 100644
--- a/src/backend/access/hash/hashsearch.c
+++ b/src/backend/access/hash/hashsearch.c
@@ -298,6 +298,7 @@ _hash_first(IndexScanDesc scan, ScanDirection dir)
 	HashScanPosItem *currItem;
 
 	pgstat_count_index_scan(rel);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index d69798795b4..415328a2633 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -955,6 +955,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
 	 * _bt_search/_bt_endpoint below
 	 */
 	pgstat_count_index_scan(rel);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index 25893050c58..79f158e49d2 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -421,6 +421,7 @@ spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
 
 	/* count an indexscan for stats */
 	pgstat_count_index_scan(scan->indexRelation);
+	pgstat_count_backend_rel_idx_scan();
 	if (scan->instrument)
 		scan->instrument->nsearches++;
 }
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index dd85b78a6dc..7193ab693fb 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -917,6 +917,7 @@ CREATE VIEW pg_stat_backend AS
             S.seq_scan,
             S.seq_tup_read,
             S.idx_tup_fetch,
+            S.idx_scan,
             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 282f62dd0d3..dc501306901 100644
--- a/src/backend/utils/activity/pgstat_backend.c
+++ b/src/backend/utils/activity/pgstat_backend.c
@@ -289,6 +289,7 @@ pgstat_flush_backend_entry_rel(PgStat_EntryRef *entry_ref)
 	BACKENDREL_ACC(heap_scan);
 	BACKENDREL_ACC(seq_tup_read);
 	BACKENDREL_ACC(idx_tup_fetch);
+	BACKENDREL_ACC(idx_scan);
 #undef BACKENDREL_ACC
 
 	/*
@@ -467,3 +468,6 @@ PGSTAT_COUNT_BACKEND_FUNC(seq_tup_read)
 
 /* pgstat_count_backend_rel_idx_tup_fetch */
 PGSTAT_COUNT_BACKEND_FUNC(idx_tup_fetch)
+
+/* pgstat_count_backend_rel_idx_scan */
+PGSTAT_COUNT_BACKEND_FUNC(idx_scan)
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index d050bcf8ed8..60298c5f8d9 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	5
+#define PG_STAT_GET_BACKEND_STATS_COLS	6
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -728,6 +728,7 @@ pg_stat_get_backend_statistics(PG_FUNCTION_ARGS)
 		values[i++] = Int64GetDatum(backend_stats->heap_scan);
 		values[i++] = Int64GetDatum(backend_stats->seq_tup_read);
 		values[i++] = Int64GetDatum(backend_stats->idx_tup_fetch);
+		values[i++] = Int64GetDatum(backend_stats->idx_scan);
 
 		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 a9709214d88..9212faa0506 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,timestamptz}',
-  proargmodes => '{i,o,o,o,o,o}',
-  proargnames => '{pid,pid,seq_scan,seq_tup_read,idx_tup_fetch,stats_reset}',
+  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}',
   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 64739d04e5a..a55811ef8c3 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -493,6 +493,7 @@ typedef struct PgStat_Backend
 	PgStat_Counter heap_scan;
 	PgStat_Counter seq_tup_read;
 	PgStat_Counter idx_tup_fetch;
+	PgStat_Counter idx_scan;
 } PgStat_Backend;
 
 typedef struct PgStat_BackendRelPending
@@ -500,6 +501,7 @@ typedef struct PgStat_BackendRelPending
 	PgStat_Counter heap_scan;
 	PgStat_Counter seq_tup_read;
 	PgStat_Counter idx_tup_fetch;
+	PgStat_Counter idx_scan;
 } PgStat_BackendRelPending;
 
 /* ---------
@@ -582,6 +584,7 @@ extern void pgstat_create_backend(ProcNumber procnum);
 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);
 
 /*
  * Functions in pgstat_bgwriter.c
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 35a0786d7b1..007631d2421 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1851,8 +1851,9 @@ pg_stat_backend| SELECT 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, stats_reset);
+   FROM pg_stat_get_backend_statistics(NULL::integer) s(pid, seq_scan, seq_tup_read, idx_tup_fetch, idx_scan, 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,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 9804cdc9c84..6f15d1da140 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -119,7 +119,8 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
  WHERE t.relname='tenk2' AND b.relname='tenk2';
 COMMIT;
 SELECT seq_scan AS seq_scan_before, seq_tup_read AS seq_tup_read_before,
-       idx_tup_fetch AS idx_tup_fetch_before
+       idx_tup_fetch AS idx_tup_fetch_before,
+       idx_scan AS idx_scan_before
   FROM pg_stat_backend WHERE pid = pg_backend_pid() \gset
 -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters
 CREATE TABLE trunc_stats_test(id serial);
@@ -223,14 +224,16 @@ SELECT st.seq_scan >= pr.seq_scan + 1,
 (1 row)
 
 SELECT seq_scan AS seq_scan_after, seq_tup_read AS seq_tup_read_after,
-       idx_tup_fetch AS idx_tup_fetch_after
+       idx_tup_fetch AS idx_tup_fetch_after,
+       idx_scan AS idx_scan_after
   FROM pg_stat_backend WHERE pid = pg_backend_pid() \gset
 SELECT :seq_scan_after > :seq_scan_before,
        :seq_tup_read_after > :seq_tup_read_before,
-       :idx_tup_fetch_after > :idx_tup_fetch_before;
- ?column? | ?column? | ?column? 
-----------+----------+----------
- t        | t        | t
+       :idx_tup_fetch_after > :idx_tup_fetch_before,
+       :idx_scan_after > :idx_scan_before;
+ ?column? | ?column? | ?column? | ?column? 
+----------+----------+----------+----------
+ t        | t        | t        | t
 (1 row)
 
 SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 279cb6f9dfb..01810b256e0 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -39,7 +39,8 @@ SELECT t.seq_scan, t.seq_tup_read, t.idx_scan, t.idx_tup_fetch,
 COMMIT;
 
 SELECT seq_scan AS seq_scan_before, seq_tup_read AS seq_tup_read_before,
-       idx_tup_fetch AS idx_tup_fetch_before
+       idx_tup_fetch AS idx_tup_fetch_before,
+       idx_scan AS idx_scan_before
   FROM pg_stat_backend WHERE pid = pg_backend_pid() \gset
 
 -- test effects of TRUNCATE on n_live_tup/n_dead_tup counters
@@ -127,12 +128,14 @@ SELECT st.seq_scan >= pr.seq_scan + 1,
  WHERE st.relname='tenk2' AND cl.relname='tenk2';
 
 SELECT seq_scan AS seq_scan_after, seq_tup_read AS seq_tup_read_after,
-       idx_tup_fetch AS idx_tup_fetch_after
+       idx_tup_fetch AS idx_tup_fetch_after,
+       idx_scan AS idx_scan_after
   FROM pg_stat_backend WHERE pid = pg_backend_pid() \gset
 
 SELECT :seq_scan_after > :seq_scan_before,
        :seq_tup_read_after > :seq_tup_read_before,
-       :idx_tup_fetch_after > :idx_tup_fetch_before;
+       :idx_tup_fetch_after > :idx_tup_fetch_before,
+       :idx_scan_after > :idx_scan_before;
 
 SELECT st.heap_blks_read + st.heap_blks_hit >= pr.heap_blks + cl.relpages,
        st.idx_blks_read + st.idx_blks_hit >= pr.idx_blks + 1
-- 
2.34.1

