Hi,

For the purpose of writing a blog post I was checking the index stats recorded for a workload, but found them rather confusing. Following along the code with the debugger it eventually made sense, and I could eventually understand what's counted.  Looking around a bit, I discovered an older discussion [1] in the mailing lists and learned that the issue is known.  The proposal in that thread is to start counting separate metadata and record stats depending on what type of index block is retrieved.

I realized those would have helped me better understand the collected index stats, so I started working on a patch to add these in the system views. Attached is a WIP patch file with partial coverage of the B-Tree index code. The implementation follows the existing stats collection approach and the naming convention proposed in [1].  Let me know if what I'm doing is feasible and if there's any concerns I could address. Next steps would be to replace all places where I currently pass in NULL with proper counting, as well as update tests and docs.

Looking forward to your feedback! Thanks!

Cheers,
Mircea

[1]: https://www.postgresql.org/message-id/flat/CAH2-WzmdZqxCS1widYzjDAM%2BZ-Jz%3DejJoaWXDVw9Qy1UsK0tLA%40mail.gmail.com
From a540a042ffb0b348254afdfdce39199900f9c7ec Mon Sep 17 00:00:00 2001
From: Mircea Cadariu <cadariu.mir...@gmail.com>
Date: Thu, 20 Feb 2025 13:45:12 +0000
Subject: [PATCH v1] Preliminary work to capture and expose separate record
 (leaf page) and metadata (non-leaf page) index access statistics in the
 system views, with partial coverage of B-Trees.

---
 contrib/amcheck/verify_heapam.c              |   2 +-
 contrib/amcheck/verify_nbtree.c              |   6 +-
 contrib/bloom/blinsert.c                     |   6 +-
 contrib/bloom/blscan.c                       |   2 +-
 contrib/bloom/blutils.c                      |   6 +-
 contrib/bloom/blvacuum.c                     |   6 +-
 contrib/pageinspect/btreefuncs.c             |   8 +-
 contrib/pageinspect/rawpage.c                |   2 +-
 contrib/pg_prewarm/autoprewarm.c             |   2 +-
 contrib/pg_surgery/heap_surgery.c            |   2 +-
 contrib/pg_visibility/pg_visibility.c        |   2 +-
 contrib/pgstattuple/pgstatapprox.c           |   2 +-
 contrib/pgstattuple/pgstatindex.c            |  10 +-
 contrib/pgstattuple/pgstattuple.c            |  10 +-
 doc/src/sgml/monitoring.sgml                 | 110 +++++++++++++++++++
 src/backend/access/brin/brin.c               |   4 +-
 src/backend/access/brin/brin_pageops.c       |   4 +-
 src/backend/access/brin/brin_revmap.c        |  12 +-
 src/backend/access/gin/ginbtree.c            |  11 +-
 src/backend/access/gin/ginfast.c             |  14 +--
 src/backend/access/gin/ginget.c              |   6 +-
 src/backend/access/gin/ginutil.c             |   6 +-
 src/backend/access/gin/ginvacuum.c           |  22 ++--
 src/backend/access/gist/gist.c               |  10 +-
 src/backend/access/gist/gistbuild.c          |  10 +-
 src/backend/access/gist/gistget.c            |   4 +-
 src/backend/access/gist/gistutil.c           |   2 +-
 src/backend/access/gist/gistvacuum.c         |   6 +-
 src/backend/access/hash/hash.c               |   3 +-
 src/backend/access/hash/hashpage.c           |  10 +-
 src/backend/access/heap/heapam.c             |  16 +--
 src/backend/access/heap/heapam_handler.c     |   9 +-
 src/backend/access/heap/hio.c                |   8 +-
 src/backend/access/heap/vacuumlazy.c         |   2 +-
 src/backend/access/heap/visibilitymap.c      |   2 +-
 src/backend/access/nbtree/nbtinsert.c        |  32 ++++--
 src/backend/access/nbtree/nbtpage.c          |  65 +++++++----
 src/backend/access/nbtree/nbtree.c           |   2 +-
 src/backend/access/nbtree/nbtsearch.c        |  34 ++++--
 src/backend/access/nbtree/nbtutils.c         |   5 +-
 src/backend/access/spgist/spgdoinsert.c      |   4 +-
 src/backend/access/spgist/spgscan.c          |   4 +-
 src/backend/access/spgist/spgutils.c         |   8 +-
 src/backend/access/spgist/spgvacuum.c        |   4 +-
 src/backend/access/transam/xloginsert.c      |   2 +-
 src/backend/catalog/system_views.sql         |  30 ++++-
 src/backend/commands/sequence.c              |   2 +-
 src/backend/storage/aio/read_stream.c        |   6 +-
 src/backend/storage/buffer/bufmgr.c          |  52 +++++----
 src/backend/storage/freespace/freespace.c    |   2 +-
 src/backend/utils/activity/pgstat_database.c |   4 +
 src/backend/utils/activity/pgstat_relation.c |   8 ++
 src/backend/utils/adt/pgstatfuncs.c          |  24 ++++
 src/include/access/nbtree.h                  |   4 +-
 src/include/catalog/pg_proc.dat              |  32 ++++++
 src/include/pgstat.h                         |  45 ++++++++
 src/include/storage/bufmgr.h                 |  12 +-
 src/test/regress/expected/rules.out          |  40 ++++++-
 58 files changed, 557 insertions(+), 201 deletions(-)

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 827312306f..5b611b15e8 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -439,7 +439,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 
                /* Read and lock the next page. */
                ctx.buffer = ReadBufferExtended(ctx.rel, MAIN_FORKNUM, 
ctx.blkno,
-                                                                               
RBM_NORMAL, ctx.bstrategy);
+                                                                               
RBM_NORMAL, ctx.bstrategy, NULL);
                LockBuffer(ctx.buffer, BUFFER_LOCK_SHARE);
                ctx.page = BufferGetPage(ctx.buffer);
 
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index aac8c74f54..5a045b1d88 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -1249,7 +1249,7 @@ bt_recheck_sibling_links(BtreeCheckState *state,
 
                /* Couple locks in the usual order for nbtree:  Left to right */
                lbuf = ReadBufferExtended(state->rel, MAIN_FORKNUM, leftcurrent,
-                                                                 RBM_NORMAL, 
state->checkstrategy);
+                                                                 RBM_NORMAL, 
state->checkstrategy, NULL);
                LockBuffer(lbuf, BT_READ);
                _bt_checkpage(state->rel, lbuf);
                page = BufferGetPage(lbuf);
@@ -1273,7 +1273,7 @@ bt_recheck_sibling_links(BtreeCheckState *state,
                {
                        newtargetbuf = ReadBufferExtended(state->rel, 
MAIN_FORKNUM,
                                                                                
          newtargetblock, RBM_NORMAL,
-                                                                               
          state->checkstrategy);
+                                                                               
          state->checkstrategy, NULL);
                        LockBuffer(newtargetbuf, BT_READ);
                        _bt_checkpage(state->rel, newtargetbuf);
                        page = BufferGetPage(newtargetbuf);
@@ -3440,7 +3440,7 @@ palloc_btree_page(BtreeCheckState *state, BlockNumber 
blocknum)
         * longer than we must.
         */
        buffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, blocknum, 
RBM_NORMAL,
-                                                               
state->checkstrategy);
+                                                               
state->checkstrategy, NULL);
        LockBuffer(buffer, BT_READ);
 
        /*
diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c
index ee8ebaf3ca..16bf039d7b 100644
--- a/contrib/bloom/blinsert.c
+++ b/contrib/bloom/blinsert.c
@@ -201,7 +201,7 @@ blinsert(Relation index, Datum *values, bool *isnull,
         * At first, try to insert new tuple to the first page in notFullPage
         * array.  If successful, we don't need to modify the meta page.
         */
-       metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+       metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO, NULL);
        LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
        metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
 
@@ -213,7 +213,7 @@ blinsert(Relation index, Datum *values, bool *isnull,
                /* Don't hold metabuffer lock while doing insert */
                LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
                LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
                state = GenericXLogStart(index);
@@ -280,7 +280,7 @@ blinsert(Relation index, Datum *values, bool *isnull,
                blkno = metaData->notFullPage[nStart];
                Assert(blkno != InvalidBlockNumber);
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
                LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
                page = GenericXLogRegisterBuffer(state, buffer, 0);
 
diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c
index bf801fe78f..30714944ac 100644
--- a/contrib/bloom/blscan.c
+++ b/contrib/bloom/blscan.c
@@ -123,7 +123,7 @@ blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
                Page            page;
 
                buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
-                                                                       blkno, 
RBM_NORMAL, bas);
+                                                                       blkno, 
RBM_NORMAL, bas, NULL);
 
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                page = BufferGetPage(buffer);
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c
index 04b61042a5..e73fe580ce 100644
--- a/contrib/bloom/blutils.c
+++ b/contrib/bloom/blutils.c
@@ -185,7 +185,7 @@ initBloomState(BloomState *state, Relation index)
 
                opts = MemoryContextAlloc(index->rd_indexcxt, 
sizeof(BloomOptions));
 
-               buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+               buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
                page = BufferGetPage(buffer);
@@ -364,7 +364,7 @@ BloomNewBuffer(Relation index)
                if (blkno == InvalidBlockNumber)
                        break;
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
 
                /*
                 * We have to guard against the possibility that someone else 
already
@@ -456,7 +456,7 @@ BloomInitMetapage(Relation index, ForkNumber forknum)
         * block number 0 (BLOOM_METAPAGE_BLKNO).  No need to hold the extension
         * lock because there cannot be concurrent inserters yet.
         */
-       metaBuffer = ReadBufferExtended(index, forknum, P_NEW, RBM_NORMAL, 
NULL);
+       metaBuffer = ReadBufferExtended(index, forknum, P_NEW, RBM_NORMAL, 
NULL, NULL);
        LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
        Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
 
diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c
index 86b15a75f6..ebe769d375 100644
--- a/contrib/bloom/blvacuum.c
+++ b/contrib/bloom/blvacuum.c
@@ -60,7 +60,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult 
*stats,
                vacuum_delay_point(false);
 
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
 
                LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
                gxlogState = GenericXLogStart(index);
@@ -139,7 +139,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult 
*stats,
         * info could already be out of date at this point, but blinsert() will
         * cope if so.
         */
-       buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
+       buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO, NULL);
        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
        gxlogState = GenericXLogStart(index);
@@ -190,7 +190,7 @@ blvacuumcleanup(IndexVacuumInfo *info, 
IndexBulkDeleteResult *stats)
                vacuum_delay_point(false);
 
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                page = (Page) BufferGetPage(buffer);
 
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 9cdc8e182b..e7c3bf2f78 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -282,7 +282,7 @@ bt_page_stats_internal(PG_FUNCTION_ARGS, enum 
pageinspect_version ext_version)
 
        bt_index_block_validate(rel, blkno);
 
-       buffer = ReadBuffer(rel, blkno);
+       buffer = ReadBuffer(rel, blkno, NULL);
        LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
        /* keep compiler quiet */
@@ -422,7 +422,7 @@ bt_multi_page_stats(PG_FUNCTION_ARGS)
                BTPageStat      stat;
                TupleDesc       tupleDesc;
 
-               buffer = ReadBuffer(rel, uargs->blkno);
+               buffer = ReadBuffer(rel, uargs->blkno, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
                /* keep compiler quiet */
@@ -651,7 +651,7 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum 
pageinspect_version ext_version)
 
                bt_index_block_validate(rel, blkno);
 
-               buffer = ReadBuffer(rel, blkno);
+               buffer = ReadBuffer(rel, blkno, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
                /*
@@ -875,7 +875,7 @@ bt_metap(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("cannot access temporary tables of 
other sessions")));
 
-       buffer = ReadBuffer(rel, 0);
+       buffer = ReadBuffer(rel, 0, NULL);
        LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
        page = BufferGetPage(buffer);
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 617dff821a..7605326c66 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -185,7 +185,7 @@ get_raw_page_internal(text *relname, ForkNumber forknum, 
BlockNumber blkno)
 
        /* Take a verbatim copy of the page */
 
-       buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL);
+       buf = ReadBufferExtended(rel, forknum, blkno, RBM_NORMAL, NULL, NULL);
        LockBuffer(buf, BUFFER_LOCK_SHARE);
 
        memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ);
diff --git a/contrib/pg_prewarm/autoprewarm.c b/contrib/pg_prewarm/autoprewarm.c
index b45755b334..df335a054a 100644
--- a/contrib/pg_prewarm/autoprewarm.c
+++ b/contrib/pg_prewarm/autoprewarm.c
@@ -533,7 +533,7 @@ autoprewarm_database_main(Datum main_arg)
 
                /* Prewarm buffer. */
                buf = ReadBufferExtended(rel, blk->forknum, blk->blocknum, 
RBM_NORMAL,
-                                                                NULL);
+                                                                NULL, NULL);
                if (BufferIsValid(buf))
                {
                        apw_state->prewarmed_blocks++;
diff --git a/contrib/pg_surgery/heap_surgery.c 
b/contrib/pg_surgery/heap_surgery.c
index 5b94b3d523..62388c6175 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -172,7 +172,7 @@ heap_force_common(FunctionCallInfo fcinfo, 
HeapTupleForceOption heap_force_opt)
                        continue;
                }
 
-               buf = ReadBuffer(rel, blkno);
+               buf = ReadBuffer(rel, blkno, NULL);
                LockBufferForCleanup(buf);
 
                page = BufferGetPage(buf);
diff --git a/contrib/pg_visibility/pg_visibility.c 
b/contrib/pg_visibility/pg_visibility.c
index 7f268a18a7..a11469660d 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -151,7 +151,7 @@ pg_visibility(PG_FUNCTION_ARGS)
        /* Here we have to explicitly check rel size ... */
        if (blkno < RelationGetNumberOfBlocks(rel))
        {
-               buffer = ReadBuffer(rel, blkno);
+               buffer = ReadBuffer(rel, blkno, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
                page = BufferGetPage(buffer);
diff --git a/contrib/pgstattuple/pgstatapprox.c 
b/contrib/pgstattuple/pgstatapprox.c
index a59ff4e9d4..46efc49522 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -94,7 +94,7 @@ statapprox_heap(Relation rel, output_type *stat)
                }
 
                buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
-                                                                RBM_NORMAL, 
bstrategy);
+                                                                RBM_NORMAL, 
bstrategy, NULL);
 
                LockBuffer(buf, BUFFER_LOCK_SHARE);
 
diff --git a/contrib/pgstattuple/pgstatindex.c 
b/contrib/pgstattuple/pgstatindex.c
index 4b9d76ec4e..a0a924acbf 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -250,7 +250,8 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
         * Read metapage
         */
        {
-               Buffer          buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 
0, RBM_NORMAL, bstrategy);
+               Buffer          buffer = ReadBufferExtended(rel, MAIN_FORKNUM, 
0, RBM_NORMAL,
+                                                                               
                bstrategy, NULL);
                Page            page = BufferGetPage(buffer);
                BTMetaPageData *metad = BTPageGetMeta(page);
 
@@ -286,7 +287,8 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo)
                CHECK_FOR_INTERRUPTS();
 
                /* Read and lock buffer */
-               buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, 
RBM_NORMAL, bstrategy);
+               buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, 
RBM_NORMAL,
+                                                                       
bstrategy, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
                page = BufferGetPage(buffer);
@@ -542,7 +544,7 @@ pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo)
        /*
         * Read metapage
         */
-       buffer = ReadBuffer(rel, GIN_METAPAGE_BLKNO);
+       buffer = ReadBuffer(rel, GIN_METAPAGE_BLKNO, NULL);
        LockBuffer(buffer, GIN_SHARE);
        page = BufferGetPage(buffer);
        metadata = GinPageGetMeta(page);
@@ -645,7 +647,7 @@ pgstathashindex(PG_FUNCTION_ARGS)
                CHECK_FOR_INTERRUPTS();
 
                buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
-                                                                bstrategy);
+                                                                bstrategy, 
NULL);
                LockBuffer(buf, BUFFER_LOCK_SHARE);
                page = (Page) BufferGetPage(buf);
 
diff --git a/contrib/pgstattuple/pgstattuple.c 
b/contrib/pgstattuple/pgstattuple.c
index 48cb8f59c4..a15668dc11 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -373,7 +373,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
                        CHECK_FOR_INTERRUPTS();
 
                        buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
-                                                                               
RBM_NORMAL, hscan->rs_strategy);
+                                                                               
RBM_NORMAL, hscan->rs_strategy, NULL);
                        LockBuffer(buffer, BUFFER_LOCK_SHARE);
                        stat.free_space += PageGetExactFreeSpace((Page) 
BufferGetPage(buffer));
                        UnlockReleaseBuffer(buffer);
@@ -386,7 +386,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
                CHECK_FOR_INTERRUPTS();
 
                buffer = ReadBufferExtended(rel, MAIN_FORKNUM, block,
-                                                                       
RBM_NORMAL, hscan->rs_strategy);
+                                                                       
RBM_NORMAL, hscan->rs_strategy, NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                stat.free_space += PageGetExactFreeSpace((Page) 
BufferGetPage(buffer));
                UnlockReleaseBuffer(buffer);
@@ -411,7 +411,8 @@ pgstat_btree_page(pgstattuple_type *stat, Relation rel, 
BlockNumber blkno,
        Buffer          buf;
        Page            page;
 
-       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy);
+       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy,
+                                                        NULL);
        LockBuffer(buf, BT_READ);
        page = BufferGetPage(buf);
 
@@ -497,7 +498,8 @@ pgstat_gist_page(pgstattuple_type *stat, Relation rel, 
BlockNumber blkno,
        Buffer          buf;
        Page            page;
 
-       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy);
+       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy,
+                                                        NULL);
        LockBuffer(buf, GIST_SHARE);
        gistcheckpage(rel, buf);
        page = BufferGetPage(buf);
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 71c4f96d05..e3d1477ef0 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3425,6 +3425,44 @@ 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>metadata_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of metadata disk blocks read in this database
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>metadata_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times metadata disk blocks were found already in the buffer
+       cache
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>record_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of record disk blocks read in this database
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>record_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of times record disk blocks were found already in the buffer
+       cache
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>blks_hit</structfield> <type>bigint</type>
@@ -4368,6 +4406,42 @@ 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_metadata_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of metadata disk blocks read from all indexes on this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_metadata_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of metadata block hits in all indexes on this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_record_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of record disk blocks read from all indexes on this table
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_record_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of record block hits in all indexes on this table
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>idx_blks_hit</structfield> <type>bigint</type>
@@ -4504,6 +4578,42 @@ 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_metadata_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of metadata disk blocks read from this index
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_metadata_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of metadata buffer hits in this index
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_record_blks_read</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of record disk blocks read from this index
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>idx_record_blks_hit</structfield> <type>bigint</type>
+      </para>
+      <para>
+       Number of record buffer hits in this index
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>idx_blks_hit</structfield> <type>bigint</type>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 60320440fc..5c0e3febe5 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -1647,7 +1647,7 @@ brinGetStats(Relation index, BrinStatsData *stats)
        Page            metapage;
        BrinMetaPageData *metadata;
 
-       metabuffer = ReadBuffer(index, BRIN_METAPAGE_BLKNO);
+       metabuffer = ReadBuffer(index, BRIN_METAPAGE_BLKNO, NULL);
        LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
        metapage = BufferGetPage(metabuffer);
        metadata = (BrinMetaPageData *) PageGetContents(metapage);
@@ -2182,7 +2182,7 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy 
strategy)
                CHECK_FOR_INTERRUPTS();
 
                buf = ReadBufferExtended(idxrel, MAIN_FORKNUM, blkno,
-                                                                RBM_NORMAL, 
strategy);
+                                                                RBM_NORMAL, 
strategy, NULL);
 
                brin_page_cleanup(idxrel, buf);
 
diff --git a/src/backend/access/brin/brin_pageops.c 
b/src/backend/access/brin/brin_pageops.c
index 6d8dd1512d..c78f0cc4e7 100644
--- a/src/backend/access/brin/brin_pageops.c
+++ b/src/backend/access/brin/brin_pageops.c
@@ -739,7 +739,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size 
itemsz,
                                LockRelationForExtension(irel, ExclusiveLock);
                                extensionLockHeld = true;
                        }
-                       buf = ReadBuffer(irel, P_NEW);
+                       buf = ReadBuffer(irel, P_NEW, NULL);
                        newblk = BufferGetBlockNumber(buf);
                        *extended = true;
 
@@ -756,7 +756,7 @@ brin_getinsertbuffer(Relation irel, Buffer oldbuf, Size 
itemsz,
                }
                else
                {
-                       buf = ReadBuffer(irel, newblk);
+                       buf = ReadBuffer(irel, newblk, NULL);
                }
 
                /*
diff --git a/src/backend/access/brin/brin_revmap.c 
b/src/backend/access/brin/brin_revmap.c
index 4e380ecc71..4a0256d96a 100644
--- a/src/backend/access/brin/brin_revmap.c
+++ b/src/backend/access/brin/brin_revmap.c
@@ -74,7 +74,7 @@ brinRevmapInitialize(Relation idxrel, BlockNumber 
*pagesPerRange)
        BrinMetaPageData *metadata;
        Page            page;
 
-       meta = ReadBuffer(idxrel, BRIN_METAPAGE_BLKNO);
+       meta = ReadBuffer(idxrel, BRIN_METAPAGE_BLKNO, NULL);
        LockBuffer(meta, BUFFER_LOCK_SHARE);
        page = BufferGetPage(meta);
        metadata = (BrinMetaPageData *) PageGetContents(page);
@@ -231,7 +231,7 @@ brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber 
heapBlk,
                                ReleaseBuffer(revmap->rm_currBuf);
 
                        Assert(mapBlk != InvalidBlockNumber);
-                       revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, 
mapBlk);
+                       revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, 
mapBlk, NULL);
                }
 
                LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_SHARE);
@@ -269,7 +269,7 @@ brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber 
heapBlk,
                {
                        if (BufferIsValid(*buf))
                                ReleaseBuffer(*buf);
-                       *buf = ReadBuffer(idxRel, blk);
+                       *buf = ReadBuffer(idxRel, blk, NULL);
                }
                LockBuffer(*buf, mode);
                page = BufferGetPage(*buf);
@@ -363,7 +363,7 @@ brinRevmapDesummarizeRange(Relation idxrel, BlockNumber 
heapBlk)
                return true;
        }
 
-       regBuf = ReadBuffer(idxrel, ItemPointerGetBlockNumber(iptr));
+       regBuf = ReadBuffer(idxrel, ItemPointerGetBlockNumber(iptr), NULL);
        LockBuffer(regBuf, BUFFER_LOCK_EXCLUSIVE);
        regPg = BufferGetPage(regBuf);
 
@@ -485,7 +485,7 @@ revmap_get_buffer(BrinRevmap *revmap, BlockNumber heapBlk)
                if (revmap->rm_currBuf != InvalidBuffer)
                        ReleaseBuffer(revmap->rm_currBuf);
 
-               revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk);
+               revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk, NULL);
        }
 
        return revmap->rm_currBuf;
@@ -553,7 +553,7 @@ revmap_physical_extend(BrinRevmap *revmap)
        nblocks = RelationGetNumberOfBlocks(irel);
        if (mapBlk < nblocks)
        {
-               buf = ReadBuffer(irel, mapBlk);
+               buf = ReadBuffer(irel, mapBlk, NULL);
                LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
                page = BufferGetPage(buf);
        }
diff --git a/src/backend/access/gin/ginbtree.c 
b/src/backend/access/gin/ginbtree.c
index 26a0bdc206..ee04a27528 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -87,7 +87,7 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
 
        stack = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
        stack->blkno = btree->rootBlkno;
-       stack->buffer = ReadBuffer(btree->index, btree->rootBlkno);
+       stack->buffer = ReadBuffer(btree->index, btree->rootBlkno, NULL);
        stack->parent = NULL;
        stack->predictNumber = 1;
 
@@ -148,7 +148,8 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
                {
                        /* in search mode we may forget path to leaf */
                        stack->blkno = child;
-                       stack->buffer = ReleaseAndReadBuffer(stack->buffer, 
btree->index, stack->blkno);
+                       stack->buffer = ReleaseAndReadBuffer(stack->buffer, 
btree->index, stack->blkno,
+                                                                               
                 NULL);
                }
                else
                {
@@ -157,7 +158,7 @@ ginFindLeafPage(GinBtree btree, bool searchMode,
                        ptr->parent = stack;
                        stack = ptr;
                        stack->blkno = child;
-                       stack->buffer = ReadBuffer(btree->index, stack->blkno);
+                       stack->buffer = ReadBuffer(btree->index, stack->blkno, 
NULL);
                        stack->predictNumber = 1;
                }
        }
@@ -182,7 +183,7 @@ ginStepRight(Buffer buffer, Relation index, int lockmode)
        bool            isData = GinPageIsData(page);
        BlockNumber blkno = GinPageGetOpaque(page)->rightlink;
 
-       nextbuffer = ReadBuffer(index, blkno);
+       nextbuffer = ReadBuffer(index, blkno, NULL);
        LockBuffer(nextbuffer, lockmode);
        UnlockReleaseBuffer(buffer);
 
@@ -314,7 +315,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack)
 
                /* Descend down to next level */
                blkno = leftmostBlkno;
-               buffer = ReadBuffer(btree->index, blkno);
+               buffer = ReadBuffer(btree->index, blkno, NULL);
        }
 }
 
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index a6d88572cc..68e5112732 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -239,7 +239,7 @@ ginHeapTupleFastInsert(GinState *ginstate, 
GinTupleCollector *collector)
        data.ntuples = 0;
        data.newRightlink = data.prevTail = InvalidBlockNumber;
 
-       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO, NULL);
        metapage = BufferGetPage(metabuffer);
 
        /*
@@ -319,7 +319,7 @@ ginHeapTupleFastInsert(GinState *ginstate, 
GinTupleCollector *collector)
                        data.prevTail = metadata->tail;
                        data.newRightlink = sublist.head;
 
-                       buffer = ReadBuffer(index, metadata->tail);
+                       buffer = ReadBuffer(index, metadata->tail, NULL);
                        LockBuffer(buffer, GIN_EXCLUSIVE);
                        page = BufferGetPage(buffer);
 
@@ -358,7 +358,7 @@ ginHeapTupleFastInsert(GinState *ginstate, 
GinTupleCollector *collector)
 
                CheckForSerializableConflictIn(index, NULL, GIN_METAPAGE_BLKNO);
 
-               buffer = ReadBuffer(index, metadata->tail);
+               buffer = ReadBuffer(index, metadata->tail, NULL);
                LockBuffer(buffer, GIN_EXCLUSIVE);
                page = BufferGetPage(buffer);
 
@@ -575,7 +575,7 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber 
newHead,
                while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != 
newHead)
                {
                        freespace[data.ndeleted] = blknoToDelete;
-                       buffers[data.ndeleted] = ReadBuffer(index, 
blknoToDelete);
+                       buffers[data.ndeleted] = ReadBuffer(index, 
blknoToDelete, NULL);
                        LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
                        page = BufferGetPage(buffers[data.ndeleted]);
 
@@ -827,7 +827,7 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
                workMemory = work_mem;
        }
 
-       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO, NULL);
        LockBuffer(metabuffer, GIN_SHARE);
        metapage = BufferGetPage(metabuffer);
        metadata = GinPageGetMeta(metapage);
@@ -850,7 +850,7 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
         * Read and lock head of pending list
         */
        blkno = metadata->head;
-       buffer = ReadBuffer(index, blkno);
+       buffer = ReadBuffer(index, blkno, NULL);
        LockBuffer(buffer, GIN_SHARE);
        page = BufferGetPage(buffer);
 
@@ -1003,7 +1003,7 @@ ginInsertCleanup(GinState *ginstate, bool full_clean,
                 * Read next page in pending list
                 */
                vacuum_delay_point(false);
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
                LockBuffer(buffer, GIN_SHARE);
                page = BufferGetPage(buffer);
        }
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 63dd1f3679..ebcab38baf 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -1480,7 +1480,7 @@ scanGetCandidate(IndexScanDesc scan, pendingPosition *pos)
                                 * current page.  So, we lock next page before 
releasing the
                                 * current one
                                 */
-                               Buffer          tmpbuf = 
ReadBuffer(scan->indexRelation, blkno);
+                               Buffer          tmpbuf = 
ReadBuffer(scan->indexRelation, blkno, NULL);
 
                                LockBuffer(tmpbuf, GIN_SHARE);
                                UnlockReleaseBuffer(pos->pendingBuffer);
@@ -1827,7 +1827,7 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, 
int64 *ntids)
                                match;
        int                     i;
        pendingPosition pos;
-       Buffer          metabuffer = ReadBuffer(scan->indexRelation, 
GIN_METAPAGE_BLKNO);
+       Buffer          metabuffer = ReadBuffer(scan->indexRelation, 
GIN_METAPAGE_BLKNO, NULL);
        Page            page;
        BlockNumber blkno;
 
@@ -1854,7 +1854,7 @@ scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, 
int64 *ntids)
                return;
        }
 
-       pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno);
+       pos.pendingBuffer = ReadBuffer(scan->indexRelation, blkno, NULL);
        LockBuffer(pos.pendingBuffer, GIN_SHARE);
        pos.firstOffset = FirstOffsetNumber;
        UnlockReleaseBuffer(metabuffer);
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index 1f9e58c4f1..c12b44eaca 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -310,7 +310,7 @@ GinNewBuffer(Relation index)
                if (blkno == InvalidBlockNumber)
                        break;
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
 
                /*
                 * We have to guard against the possibility that someone else 
already
@@ -627,7 +627,7 @@ ginGetStats(Relation index, GinStatsData *stats)
        Page            metapage;
        GinMetaPageData *metadata;
 
-       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO, NULL);
        LockBuffer(metabuffer, GIN_SHARE);
        metapage = BufferGetPage(metabuffer);
        metadata = GinPageGetMeta(metapage);
@@ -654,7 +654,7 @@ ginUpdateStats(Relation index, const GinStatsData *stats, 
bool is_build)
        Page            metapage;
        GinMetaPageData *metadata;
 
-       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
+       metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO, NULL);
        LockBuffer(metabuffer, GIN_EXCLUSIVE);
        metapage = BufferGetPage(metabuffer);
        metadata = GinPageGetMeta(metapage);
diff --git a/src/backend/access/gin/ginvacuum.c 
b/src/backend/access/gin/ginvacuum.c
index fbbe3a6dd7..250bffa49a 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -143,11 +143,11 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber 
deleteBlkno, BlockNumber leftBlkn
         * deletable, parent and left pages.
         */
        lBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, leftBlkno,
-                                                                RBM_NORMAL, 
gvs->strategy);
+                                                                RBM_NORMAL, 
gvs->strategy, NULL);
        dBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, deleteBlkno,
-                                                                RBM_NORMAL, 
gvs->strategy);
+                                                                RBM_NORMAL, 
gvs->strategy, NULL);
        pBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, parentBlkno,
-                                                                RBM_NORMAL, 
gvs->strategy);
+                                                                RBM_NORMAL, 
gvs->strategy, NULL);
 
        page = BufferGetPage(dBuffer);
        rightlink = GinPageGetOpaque(page)->rightlink;
@@ -270,7 +270,7 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, 
bool isRoot,
        }
 
        buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
-                                                               RBM_NORMAL, 
gvs->strategy);
+                                                               RBM_NORMAL, 
gvs->strategy, NULL);
 
        if (!isRoot)
                LockBuffer(buffer, GIN_EXCLUSIVE);
@@ -355,7 +355,7 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber 
blkno)
                PostingItem *pitem;
 
                buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, gvs->strategy);
+                                                                       
RBM_NORMAL, gvs->strategy, NULL);
                LockBuffer(buffer, GIN_SHARE);
                page = BufferGetPage(buffer);
 
@@ -396,7 +396,7 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber 
blkno)
                        break;
 
                buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, gvs->strategy);
+                                                                       
RBM_NORMAL, gvs->strategy, NULL);
                LockBuffer(buffer, GIN_EXCLUSIVE);
                page = BufferGetPage(buffer);
        }
@@ -419,7 +419,7 @@ ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber 
rootBlkno)
                                   *tmp;
 
                buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, rootBlkno,
-                                                                       
RBM_NORMAL, gvs->strategy);
+                                                                       
RBM_NORMAL, gvs->strategy, NULL);
 
                /*
                 * Lock posting tree root for cleanup to ensure there are no
@@ -598,7 +598,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult 
*stats,
        gvs.result = stats;
 
        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                               RBM_NORMAL, 
info->strategy);
+                                                               RBM_NORMAL, 
info->strategy, NULL);
 
        /* find leaf page */
        for (;;)
@@ -631,7 +631,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult 
*stats,
 
                UnlockReleaseBuffer(buffer);
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
        }
 
        /* right now we found leftmost page in entry's BTree */
@@ -674,7 +674,7 @@ ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult 
*stats,
                        break;
 
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
                LockBuffer(buffer, GIN_EXCLUSIVE);
        }
 
@@ -751,7 +751,7 @@ ginvacuumcleanup(IndexVacuumInfo *info, 
IndexBulkDeleteResult *stats)
                vacuum_delay_point(false);
 
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
                LockBuffer(buffer, GIN_SHARE);
                page = (Page) BufferGetPage(buffer);
 
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 4d858b65e1..d393f0c731 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -681,7 +681,7 @@ gistdoinsert(Relation r, IndexTuple itup, Size freespace,
                }
 
                if (XLogRecPtrIsInvalid(stack->lsn))
-                       stack->buffer = ReadBuffer(state.r, stack->blkno);
+                       stack->buffer = ReadBuffer(state.r, stack->blkno, NULL);
 
                /*
                 * Be optimistic and grab shared lock first. Swap it for an 
exclusive
@@ -932,7 +932,7 @@ gistFindPath(Relation r, BlockNumber child, OffsetNumber 
*downlinkoffnum)
                top = linitial(fifo);
                fifo = list_delete_first(fifo);
 
-               buffer = ReadBuffer(r, top->blkno);
+               buffer = ReadBuffer(r, top->blkno, NULL);
                LockBuffer(buffer, GIST_SHARE);
                gistcheckpage(r, buffer);
                page = (Page) BufferGetPage(buffer);
@@ -1085,7 +1085,7 @@ gistFindCorrectParent(Relation r, GISTInsertStack *child, 
bool is_build)
                         */
                        break;
                }
-               parent->buffer = ReadBuffer(r, parent->blkno);
+               parent->buffer = ReadBuffer(r, parent->blkno, NULL);
                LockBuffer(parent->buffer, GIST_EXCLUSIVE);
                gistcheckpage(r, parent->buffer);
                parent->page = (Page) BufferGetPage(parent->buffer);
@@ -1110,7 +1110,7 @@ gistFindCorrectParent(Relation r, GISTInsertStack *child, 
bool is_build)
        /* note we don't lock them or gistcheckpage them here! */
        while (ptr)
        {
-               ptr->buffer = ReadBuffer(r, ptr->blkno);
+               ptr->buffer = ReadBuffer(r, ptr->blkno, NULL);
                ptr->page = (Page) BufferGetPage(ptr->buffer);
                ptr = ptr->parent;
        }
@@ -1225,7 +1225,7 @@ gistfixsplit(GISTInsertState *state, GISTSTATE *giststate)
                if (GistFollowRight(page))
                {
                        /* lock next page */
-                       buf = ReadBuffer(state->r, 
GistPageGetOpaque(page)->rightlink);
+                       buf = ReadBuffer(state->r, 
GistPageGetOpaque(page)->rightlink, NULL);
                        LockBuffer(buf, GIST_EXCLUSIVE);
                }
                else
diff --git a/src/backend/access/gist/gistbuild.c 
b/src/backend/access/gist/gistbuild.c
index 9e707167d9..20a2310dfc 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -966,7 +966,7 @@ gistProcessItup(GISTBuildState *buildstate, IndexTuple itup,
                 * descend down to.
                 */
 
-               buffer = ReadBuffer(indexrel, blkno);
+               buffer = ReadBuffer(indexrel, blkno, NULL);
                LockBuffer(buffer, GIST_EXCLUSIVE);
 
                page = (Page) BufferGetPage(buffer);
@@ -1029,7 +1029,7 @@ gistProcessItup(GISTBuildState *buildstate, IndexTuple 
itup,
                 * We've reached a leaf page. Place the tuple here.
                 */
                Assert(level == 0);
-               buffer = ReadBuffer(indexrel, blkno);
+               buffer = ReadBuffer(indexrel, blkno, NULL);
                LockBuffer(buffer, GIST_EXCLUSIVE);
                gistbufferinginserttuples(buildstate, buffer, level,
                                                                  &itup, 1, 
InvalidOffsetNumber,
@@ -1102,7 +1102,7 @@ gistbufferinginserttuples(GISTBuildState *buildstate, 
Buffer buffer, int level,
                                ItemId          iid = PageGetItemId(page, off);
                                IndexTuple      idxtuple = (IndexTuple) 
PageGetItem(page, iid);
                                BlockNumber childblkno = 
ItemPointerGetBlockNumber(&(idxtuple->t_tid));
-                               Buffer          childbuf = 
ReadBuffer(buildstate->indexrel, childblkno);
+                               Buffer          childbuf = 
ReadBuffer(buildstate->indexrel, childblkno, NULL);
 
                                LockBuffer(childbuf, GIST_SHARE);
                                gistMemorizeAllDownlinks(buildstate, childbuf);
@@ -1246,7 +1246,7 @@ gistBufferingFindCorrectParent(GISTBuildState *buildstate,
                parent = *parentblkno;
        }
 
-       buffer = ReadBuffer(buildstate->indexrel, parent);
+       buffer = ReadBuffer(buildstate->indexrel, parent, NULL);
        page = BufferGetPage(buffer);
        LockBuffer(buffer, GIST_EXCLUSIVE);
        gistcheckpage(buildstate->indexrel, buffer);
@@ -1441,7 +1441,7 @@ gistGetMaxLevel(Relation index)
                Page            page;
                IndexTuple      itup;
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
 
                /*
                 * There's no concurrent access during index build, so locking 
is just
diff --git a/src/backend/access/gist/gistget.c 
b/src/backend/access/gist/gistget.c
index cc40e928e0..98c9356b4b 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -49,7 +49,7 @@ gistkillitems(IndexScanDesc scan)
        Assert(!XLogRecPtrIsInvalid(so->curPageLSN));
        Assert(so->killedItems != NULL);
 
-       buffer = ReadBuffer(scan->indexRelation, so->curBlkno);
+       buffer = ReadBuffer(scan->indexRelation, so->curBlkno, NULL);
        if (!BufferIsValid(buffer))
                return;
 
@@ -340,7 +340,7 @@ gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem,
 
        Assert(!GISTSearchItemIsHeap(*pageItem));
 
-       buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
+       buffer = ReadBuffer(scan->indexRelation, pageItem->blkno, NULL);
        LockBuffer(buffer, GIST_SHARE);
        PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
        gistcheckpage(scan->indexRelation, buffer);
diff --git a/src/backend/access/gist/gistutil.c 
b/src/backend/access/gist/gistutil.c
index dbc4ac639a..b4dcc1056f 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -833,7 +833,7 @@ gistNewBuffer(Relation r, Relation heaprel)
                if (blkno == InvalidBlockNumber)
                        break;                          /* nothing left in FSM 
*/
 
-               buffer = ReadBuffer(r, blkno);
+               buffer = ReadBuffer(r, blkno, NULL);
 
                /*
                 * We have to guard against the possibility that someone else 
already
diff --git a/src/backend/access/gist/gistvacuum.c 
b/src/backend/access/gist/gistvacuum.c
index dd0d9d5006..f95e42f40a 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -286,7 +286,7 @@ restart:
        vacuum_delay_point(false);
 
        buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
-                                                               info->strategy);
+                                                               info->strategy, 
NULL);
 
        /*
         * We are not going to stay here for a long time, aggressively grab an
@@ -482,7 +482,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, 
GistVacState *vstate)
                int                     deleted;
 
                buffer = ReadBufferExtended(rel, MAIN_FORKNUM, (BlockNumber) 
blkno,
-                                                                       
RBM_NORMAL, info->strategy);
+                                                                       
RBM_NORMAL, info->strategy, NULL);
 
                LockBuffer(buffer, GIST_SHARE);
                page = (Page) BufferGetPage(buffer);
@@ -548,7 +548,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, 
GistVacState *vstate)
                                break;
 
                        leafbuf = ReadBufferExtended(rel, MAIN_FORKNUM, 
leafs_to_delete[i],
-                                                                               
 RBM_NORMAL, info->strategy);
+                                                                               
 RBM_NORMAL, info->strategy, NULL);
                        LockBuffer(leafbuf, GIST_EXCLUSIVE);
                        gistcheckpage(rel, leafbuf);
 
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 02ec1126a4..c806df1eb9 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -512,7 +512,8 @@ loop_top:
                 * We need to acquire a cleanup lock on the primary bucket page 
to out
                 * wait concurrent scans before deleting the dead tuples.
                 */
-               buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
info->strategy);
+               buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
info->strategy,
+                                                                NULL);
                LockBufferForCleanup(buf);
                _hash_checkpage(rel, buf, LH_BUCKET_PAGE);
 
diff --git a/src/backend/access/hash/hashpage.c 
b/src/backend/access/hash/hashpage.c
index b8e5bd005e..33dbcfd2f1 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -74,7 +74,7 @@ _hash_getbuf(Relation rel, BlockNumber blkno, int access, int 
flags)
        if (blkno == P_NEW)
                elog(ERROR, "hash AM does not use P_NEW");
 
-       buf = ReadBuffer(rel, blkno);
+       buf = ReadBuffer(rel, blkno, NULL);
 
        if (access != HASH_NOLOCK)
                LockBuffer(buf, access);
@@ -100,7 +100,7 @@ _hash_getbuf_with_condlock_cleanup(Relation rel, 
BlockNumber blkno, int flags)
        if (blkno == P_NEW)
                elog(ERROR, "hash AM does not use P_NEW");
 
-       buf = ReadBuffer(rel, blkno);
+       buf = ReadBuffer(rel, blkno, NULL);
 
        if (!ConditionalLockBufferForCleanup(buf))
        {
@@ -140,7 +140,7 @@ _hash_getinitbuf(Relation rel, BlockNumber blkno)
                elog(ERROR, "hash AM does not use P_NEW");
 
        buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK,
-                                                        NULL);
+                                                        NULL, NULL);
 
        /* ref count and lock type are correct */
 
@@ -218,7 +218,7 @@ _hash_getnewbuf(Relation rel, BlockNumber blkno, ForkNumber 
forkNum)
        else
        {
                buf = ReadBufferExtended(rel, forkNum, blkno, RBM_ZERO_AND_LOCK,
-                                                                NULL);
+                                                                NULL, NULL);
        }
 
        /* ref count and lock type are correct */
@@ -245,7 +245,7 @@ _hash_getbuf_with_strategy(Relation rel, BlockNumber blkno,
        if (blkno == P_NEW)
                elog(ERROR, "hash AM does not use P_NEW");
 
-       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy);
+       buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, 
bstrategy, NULL);
 
        if (access != HASH_NOLOCK)
                LockBuffer(buf, access);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index fa7935a0ed..5a0aa9998c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1538,7 +1538,7 @@ heap_fetch(Relation relation,
        /*
         * Fetch and pin the appropriate page of the relation.
         */
-       buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+       buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid), NULL);
 
        /*
         * Need share lock on buffer to examine tuple commit status.
@@ -1832,7 +1832,7 @@ heap_get_latest_tid(TableScanDesc sscan,
                /*
                 * Read, pin, and lock the page.
                 */
-               buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+               buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid), 
NULL);
                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                page = BufferGetPage(buffer);
 
@@ -2728,7 +2728,7 @@ heap_delete(Relation relation, ItemPointer tid,
                                 errmsg("cannot delete tuples during a parallel 
operation")));
 
        block = ItemPointerGetBlockNumber(tid);
-       buffer = ReadBuffer(relation, block);
+       buffer = ReadBuffer(relation, block, NULL);
        page = BufferGetPage(buffer);
 
        /*
@@ -3257,7 +3257,7 @@ heap_update(Relation relation, ItemPointer otid, 
HeapTuple newtup,
 
        block = ItemPointerGetBlockNumber(otid);
        INJECTION_POINT("heap_update-before-pin");
-       buffer = ReadBuffer(relation, block);
+       buffer = ReadBuffer(relation, block, NULL);
        page = BufferGetPage(buffer);
 
        /*
@@ -4513,7 +4513,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
        bool            have_tuple_lock = false;
        bool            cleared_all_frozen = false;
 
-       *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+       *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid), NULL);
        block = ItemPointerGetBlockNumber(tid);
 
        /*
@@ -6009,7 +6009,7 @@ heap_finish_speculative(Relation relation, ItemPointer 
tid)
        ItemId          lp = NULL;
        HeapTupleHeader htup;
 
-       buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
+       buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid), NULL);
        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
        page = (Page) BufferGetPage(buffer);
 
@@ -6100,7 +6100,7 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
        Assert(ItemPointerIsValid(tid));
 
        block = ItemPointerGetBlockNumber(tid);
-       buffer = ReadBuffer(relation, block);
+       buffer = ReadBuffer(relation, block, NULL);
        page = BufferGetPage(buffer);
 
        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
@@ -8181,7 +8181,7 @@ heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp 
*delstate)
                                UnlockReleaseBuffer(buf);
 
                        blkno = ItemPointerGetBlockNumber(htid);
-                       buf = ReadBuffer(rel, blkno);
+                       buf = ReadBuffer(rel, blkno, NULL);
                        nblocksaccessed++;
                        Assert(!delstate->bottomup ||
                                   nblocksaccessed <= BOTTOMUP_MAX_NBLOCKS);
diff --git a/src/backend/access/heap/heapam_handler.c 
b/src/backend/access/heap/heapam_handler.c
index c0bec01415..12167d9bb5 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -128,7 +128,8 @@ heapam_index_fetch_tuple(struct IndexFetchTableData *scan,
 
                hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf,
                                                                                
          hscan->xs_base.rel,
-                                                                               
          ItemPointerGetBlockNumber(tid));
+                                                                               
          ItemPointerGetBlockNumber(tid),
+                                                                               
          NULL);
 
                /*
                 * Prune page, but only if we weren't already on this page
@@ -2185,7 +2186,8 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
         */
        hscan->rs_cbuf = ReleaseAndReadBuffer(hscan->rs_cbuf,
                                                                                
  scan->rs_rd,
-                                                                               
  block);
+                                                                               
  block,
+                                                                               
  NULL);
        hscan->rs_cblock = block;
        buffer = hscan->rs_cbuf;
        snapshot = scan->rs_snapshot;
@@ -2414,7 +2416,8 @@ heapam_scan_sample_next_block(TableScanDesc scan, 
SampleScanState *scanstate)
 
        /* Read page using selected strategy */
        hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM,
-                                                                               
blockno, RBM_NORMAL, hscan->rs_strategy);
+                                                                               
blockno, RBM_NORMAL, hscan->rs_strategy,
+                                                                               
NULL);
 
        /* in pagemode, prune the page and determine visible tuple offsets */
        if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index c482c9d61b..7d5afcc6bc 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -93,7 +93,7 @@ ReadBufferBI(Relation relation, BlockNumber targetBlock,
        /* If not bulk-insert, exactly like ReadBuffer */
        if (!bistate)
                return ReadBufferExtended(relation, MAIN_FORKNUM, targetBlock,
-                                                                 mode, NULL);
+                                                                 mode, NULL, 
NULL);
 
        /* If we have the desired block already pinned, re-pin and return it */
        if (bistate->current_buf != InvalidBuffer)
@@ -117,7 +117,7 @@ ReadBufferBI(Relation relation, BlockNumber targetBlock,
 
        /* Perform a read using the buffer strategy */
        buffer = ReadBufferExtended(relation, MAIN_FORKNUM, targetBlock,
-                                                               mode, 
bistate->strategy);
+                                                               mode, 
bistate->strategy, NULL);
 
        /* Save the selected block as target for future inserts */
        IncrBufferRefCount(buffer);
@@ -640,7 +640,7 @@ loop:
                else if (otherBlock < targetBlock)
                {
                        /* lock other buffer first */
-                       buffer = ReadBuffer(relation, targetBlock);
+                       buffer = ReadBuffer(relation, targetBlock, NULL);
                        if (PageIsAllVisible(BufferGetPage(buffer)))
                                visibilitymap_pin(relation, targetBlock, 
vmbuffer);
                        LockBuffer(otherBuffer, BUFFER_LOCK_EXCLUSIVE);
@@ -649,7 +649,7 @@ loop:
                else
                {
                        /* lock target buffer first */
-                       buffer = ReadBuffer(relation, targetBlock);
+                       buffer = ReadBuffer(relation, targetBlock, NULL);
                        if (PageIsAllVisible(BufferGetPage(buffer)))
                                visibilitymap_pin(relation, targetBlock, 
vmbuffer);
                        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
diff --git a/src/backend/access/heap/vacuumlazy.c 
b/src/backend/access/heap/vacuumlazy.c
index 1af18a78a2..4e63579b61 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -3394,7 +3394,7 @@ count_nondeletable_pages(LVRelState *vacrel, bool 
*lock_waiter_detected)
                }
 
                buf = ReadBufferExtended(vacrel->rel, MAIN_FORKNUM, blkno, 
RBM_NORMAL,
-                                                                
vacrel->bstrategy);
+                                                                
vacrel->bstrategy, NULL);
 
                /* In this phase we only need shared access to the buffer */
                LockBuffer(buf, BUFFER_LOCK_SHARE);
diff --git a/src/backend/access/heap/visibilitymap.c 
b/src/backend/access/heap/visibilitymap.c
index 745a04ef26..a31f3098de 100644
--- a/src/backend/access/heap/visibilitymap.c
+++ b/src/backend/access/heap/visibilitymap.c
@@ -582,7 +582,7 @@ vm_readbuf(Relation rel, BlockNumber blkno, bool extend)
        }
        else
                buf = ReadBufferExtended(rel, VISIBILITYMAP_FORKNUM, blkno,
-                                                                
RBM_ZERO_ON_ERROR, NULL);
+                                                                
RBM_ZERO_ON_ERROR, NULL, NULL);
 
        /*
         * Initializing the page when needed is trickier than it looks, because 
of
diff --git a/src/backend/access/nbtree/nbtinsert.c 
b/src/backend/access/nbtree/nbtinsert.c
index 31fe1c3ade..c76f1fd2d5 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -21,6 +21,7 @@
 #include "access/xloginsert.h"
 #include "common/int.h"
 #include "common/pg_prng.h"
+#include "pgstat.h"
 #include "lib/qunique.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
@@ -323,7 +324,7 @@ _bt_search_insert(Relation rel, Relation heaprel, 
BTInsertState insertstate)
        if (RelationGetTargetBlock(rel) != InvalidBlockNumber)
        {
                /* Simulate a _bt_getbuf() call with conditional locking */
-               insertstate->buf = ReadBuffer(rel, RelationGetTargetBlock(rel));
+               insertstate->buf = ReadBuffer(rel, RelationGetTargetBlock(rel), 
NULL);
                if (_bt_conditionallockbuf(rel, insertstate->buf))
                {
                        Page            page;
@@ -733,7 +734,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, 
Relation heapRel,
                        {
                                BlockNumber nblkno = opaque->btpo_next;
 
-                               nbuf = _bt_relandgetbuf(rel, nbuf, nblkno, 
BT_READ);
+                               nbuf = _bt_relandgetbuf(rel, nbuf, nblkno, 
BT_READ, NULL);
                                page = BufferGetPage(nbuf);
                                opaque = BTPageGetOpaque(page);
                                if (!P_IGNORE(opaque))
@@ -1040,7 +1041,9 @@ _bt_stepright(Relation rel, Relation heaprel, 
BTInsertState insertstate,
        rblkno = opaque->btpo_next;
        for (;;)
        {
-               rbuf = _bt_relandgetbuf(rel, rbuf, rblkno, BT_WRITE);
+               bool hit;
+               rbuf = _bt_relandgetbuf(rel, rbuf, rblkno, BT_WRITE, &hit);
+               pgstat_count_buffer(rel, P_ISLEAF(opaque), hit);
                page = BufferGetPage(rbuf);
                opaque = BTPageGetOpaque(page);
 
@@ -1256,10 +1259,14 @@ _bt_insertonpg(Relation rel,
                 */
                if (unlikely(split_only_page))
                {
+                       bool hit;
+
                        Assert(!isleaf);
                        Assert(BufferIsValid(cbuf));
 
-                       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+                       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE, 
&hit);
+                       pgstat_count_buffer(rel, true, hit);
+
                        metapg = BufferGetPage(metabuf);
                        metad = BTPageGetMeta(metapg);
 
@@ -1890,7 +1897,9 @@ _bt_split(Relation rel, Relation heaprel, BTScanInsert 
itup_key, Buffer buf,
         */
        if (!isrightmost)
        {
-               sbuf = _bt_getbuf(rel, oopaque->btpo_next, BT_WRITE);
+               bool hit;
+               sbuf = _bt_getbuf(rel, oopaque->btpo_next, BT_WRITE, &hit);
+               pgstat_count_buffer(rel, P_ISLEAF(oopaque), hit);
                spage = BufferGetPage(sbuf);
                sopaque = BTPageGetOpaque(spage);
                if (sopaque->btpo_prev != origpagenumber)
@@ -2247,12 +2256,14 @@ _bt_finish_split(Relation rel, Relation heaprel, Buffer 
lbuf, BTStack stack)
        BTPageOpaque rpageop;
        bool            wasroot;
        bool            wasonly;
+       bool        hit;
 
        Assert(P_INCOMPLETE_SPLIT(lpageop));
        Assert(heaprel != NULL);
 
        /* Lock right sibling, the one missing the downlink */
-       rbuf = _bt_getbuf(rel, lpageop->btpo_next, BT_WRITE);
+       rbuf = _bt_getbuf(rel, lpageop->btpo_next, BT_WRITE, &hit);
+       pgstat_count_buffer(rel, P_ISLEAF(lpageop), hit);
        rpage = BufferGetPage(rbuf);
        rpageop = BTPageGetOpaque(rpage);
 
@@ -2264,7 +2275,8 @@ _bt_finish_split(Relation rel, Relation heaprel, Buffer 
lbuf, BTStack stack)
                BTMetaPageData *metad;
 
                /* acquire lock on the metapage */
-               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE, &hit);
+               pgstat_count_buffer(rel, true, hit);
                metapg = BufferGetPage(metabuf);
                metad = BTPageGetMeta(metapg);
 
@@ -2330,7 +2342,7 @@ _bt_getstackbuf(Relation rel, Relation heaprel, BTStack 
stack, BlockNumber child
                Page            page;
                BTPageOpaque opaque;
 
-               buf = _bt_getbuf(rel, blkno, BT_WRITE);
+               buf = _bt_getbuf(rel, blkno, BT_WRITE, NULL);
                page = BufferGetPage(buf);
                opaque = BTPageGetOpaque(page);
 
@@ -2460,6 +2472,7 @@ _bt_newlevel(Relation rel, Relation heaprel, Buffer lbuf, 
Buffer rbuf)
        Buffer          metabuf;
        Page            metapg;
        BTMetaPageData *metad;
+       bool        hit;
 
        lbkno = BufferGetBlockNumber(lbuf);
        rbkno = BufferGetBlockNumber(rbuf);
@@ -2472,7 +2485,8 @@ _bt_newlevel(Relation rel, Relation heaprel, Buffer lbuf, 
Buffer rbuf)
        rootblknum = BufferGetBlockNumber(rootbuf);
 
        /* acquire lock on the metapage */
-       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE, &hit);
+       pgstat_count_buffer(rel, true, hit);
        metapg = BufferGetPage(metabuf);
        metad = BTPageGetMeta(metapg);
 
diff --git a/src/backend/access/nbtree/nbtpage.c 
b/src/backend/access/nbtree/nbtpage.c
index c79dd38ee1..9bcb341470 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -30,6 +30,7 @@
 #include "access/xloginsert.h"
 #include "common/int.h"
 #include "miscadmin.h"
+#include "pgstat.h"
 #include "storage/indexfsm.h"
 #include "storage/predicate.h"
 #include "storage/procarray.h"
@@ -183,13 +184,15 @@ _bt_vacuum_needs_cleanup(Relation rel)
        BTMetaPageData *metad;
        uint32          btm_version;
        BlockNumber prev_num_delpages;
+       bool        hit;
 
        /*
         * Copy details from metapage to local variables quickly.
         *
         * Note that we deliberately avoid using cached version of metapage 
here.
         */
-       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+       pgstat_count_metadata_buffer(rel, hit);
        metapg = BufferGetPage(metabuf);
        metad = BTPageGetMeta(metapg);
        btm_version = metad->btm_version;
@@ -234,6 +237,7 @@ _bt_set_cleanup_info(Relation rel, BlockNumber num_delpages)
        Buffer          metabuf;
        Page            metapg;
        BTMetaPageData *metad;
+       bool        hit;
 
        /*
         * On-disk compatibility note: The btm_last_cleanup_num_delpages 
metapage
@@ -253,7 +257,8 @@ _bt_set_cleanup_info(Relation rel, BlockNumber num_delpages)
         * no longer used as of PostgreSQL 14.  We set it to -1.0 on rewrite, 
just
         * to be consistent.
         */
-       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+       pgstat_count_metadata_buffer(rel, hit);
        metapg = BufferGetPage(metabuf);
        metad = BTPageGetMeta(metapg);
 
@@ -350,6 +355,7 @@ _bt_getroot(Relation rel, Relation heaprel, int access)
        BlockNumber rootblkno;
        uint32          rootlevel;
        BTMetaPageData *metad;
+       bool        hit;
 
        Assert(access == BT_READ || heaprel != NULL);
 
@@ -373,7 +379,8 @@ _bt_getroot(Relation rel, Relation heaprel, int access)
                Assert(rootblkno != P_NONE);
                rootlevel = metad->btm_fastlevel;
 
-               rootbuf = _bt_getbuf(rel, rootblkno, BT_READ);
+               rootbuf = _bt_getbuf(rel, rootblkno, BT_READ, &hit);
+               pgstat_count_metadata_buffer(rel, hit);
                rootpage = BufferGetPage(rootbuf);
                rootopaque = BTPageGetOpaque(rootpage);
 
@@ -399,7 +406,8 @@ _bt_getroot(Relation rel, Relation heaprel, int access)
                rel->rd_amcache = NULL;
        }
 
-       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+       pgstat_count_metadata_buffer(rel, hit);
        metad = _bt_getmeta(rel, metabuf);
 
        /* if no root page initialized yet, do it */
@@ -535,7 +543,8 @@ _bt_getroot(Relation rel, Relation heaprel, int access)
 
                for (;;)
                {
-                       rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, 
BT_READ);
+                       rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, 
BT_READ, &hit);
+                       pgstat_count_metadata_buffer(rel, hit);
                        rootpage = BufferGetPage(rootbuf);
                        rootopaque = BTPageGetOpaque(rootpage);
 
@@ -588,6 +597,7 @@ _bt_gettrueroot(Relation rel)
        BlockNumber rootblkno;
        uint32          rootlevel;
        BTMetaPageData *metad;
+       bool        hit;
 
        /*
         * We don't try to use cached metapage data here, since (a) this path is
@@ -599,7 +609,8 @@ _bt_gettrueroot(Relation rel)
                pfree(rel->rd_amcache);
        rel->rd_amcache = NULL;
 
-       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+       pgstat_count_metadata_buffer(rel, hit);
        metapg = BufferGetPage(metabuf);
        metaopaque = BTPageGetOpaque(metapg);
        metad = BTPageGetMeta(metapg);
@@ -638,7 +649,8 @@ _bt_gettrueroot(Relation rel)
 
        for (;;)
        {
-               rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
+               rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ, 
&hit);
+               pgstat_count_metadata_buffer(rel, hit);
                rootpage = BufferGetPage(rootbuf);
                rootopaque = BTPageGetOpaque(rootpage);
 
@@ -679,8 +691,10 @@ _bt_getrootheight(Relation rel)
        if (rel->rd_amcache == NULL)
        {
                Buffer          metabuf;
+               bool        hit;
 
-               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+               pgstat_count_metadata_buffer(rel, hit);
                metad = _bt_getmeta(rel, metabuf);
 
                /*
@@ -743,8 +757,10 @@ _bt_metaversion(Relation rel, bool *heapkeyspace, bool 
*allequalimage)
        if (rel->rd_amcache == NULL)
        {
                Buffer          metabuf;
+               bool        hit;
 
-               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
+               metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ, &hit);
+               pgstat_count_metadata_buffer(rel, hit);
                metad = _bt_getmeta(rel, metabuf);
 
                /*
@@ -842,14 +858,14 @@ _bt_checkpage(Relation rel, Buffer buf)
  *             as _bt_lockbuf().
  */
 Buffer
-_bt_getbuf(Relation rel, BlockNumber blkno, int access)
+_bt_getbuf(Relation rel, BlockNumber blkno, int access, bool *hit)
 {
        Buffer          buf;
 
        Assert(BlockNumberIsValid(blkno));
 
        /* Read an existing block of the relation */
-       buf = ReadBuffer(rel, blkno);
+       buf = ReadBuffer(rel, blkno, hit);
        _bt_lockbuf(rel, buf, access);
        _bt_checkpage(rel, buf);
 
@@ -903,7 +919,7 @@ _bt_allocbuf(Relation rel, Relation heaprel)
                blkno = GetFreeIndexPage(rel);
                if (blkno == InvalidBlockNumber)
                        break;
-               buf = ReadBuffer(rel, blkno);
+               buf = ReadBuffer(rel, blkno, NULL);
                if (_bt_conditionallockbuf(rel, buf))
                {
                        page = BufferGetPage(buf);
@@ -1000,14 +1016,14 @@ _bt_allocbuf(Relation rel, Relation heaprel)
  * is when the target page is the same one already in the buffer.
  */
 Buffer
-_bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
+_bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access, 
bool *hit)
 {
        Buffer          buf;
 
        Assert(BlockNumberIsValid(blkno));
        if (BufferIsValid(obuf))
                _bt_unlockbuf(rel, obuf);
-       buf = ReleaseAndReadBuffer(obuf, rel, blkno);
+       buf = ReleaseAndReadBuffer(obuf, rel, blkno, hit);
        _bt_lockbuf(rel, buf, access);
 
        _bt_checkpage(rel, buf);
@@ -1703,7 +1719,7 @@ _bt_leftsib_splitflag(Relation rel, BlockNumber leftsib, 
BlockNumber target)
        if (leftsib == P_NONE)
                return false;
 
-       buf = _bt_getbuf(rel, leftsib, BT_READ);
+       buf = _bt_getbuf(rel, leftsib, BT_READ, NULL);
        page = BufferGetPage(buf);
        opaque = BTPageGetOpaque(page);
 
@@ -1758,7 +1774,7 @@ _bt_rightsib_halfdeadflag(Relation rel, BlockNumber 
leafrightsib)
 
        Assert(leafrightsib != P_NONE);
 
-       buf = _bt_getbuf(rel, leafrightsib, BT_READ);
+       buf = _bt_getbuf(rel, leafrightsib, BT_READ, NULL);
        page = BufferGetPage(buf);
        opaque = BTPageGetOpaque(page);
 
@@ -2062,7 +2078,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf, BTVacState 
*vstate)
                if (!rightsib_empty)
                        break;
 
-               leafbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
+               leafbuf = _bt_getbuf(rel, rightsib, BT_WRITE, NULL);
        }
 }
 
@@ -2335,6 +2351,7 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
        uint32          targetlevel;
        IndexTuple      leafhikey;
        BlockNumber leaftopparent;
+       bool        hit;
 
        page = BufferGetPage(leafbuf);
        opaque = BTPageGetOpaque(page);
@@ -2374,7 +2391,8 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
                Assert(target != leafblkno);
 
                /* Fetch the block number of the target's left sibling */
-               buf = _bt_getbuf(rel, target, BT_READ);
+               buf = _bt_getbuf(rel, target, BT_READ, &hit);
+               pgstat_count_metadata_buffer(rel, hit);
                page = BufferGetPage(buf);
                opaque = BTPageGetOpaque(page);
                leftsib = opaque->btpo_prev;
@@ -2401,7 +2419,7 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
                _bt_lockbuf(rel, leafbuf, BT_WRITE);
        if (leftsib != P_NONE)
        {
-               lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
+               lbuf = _bt_getbuf(rel, leftsib, BT_WRITE, NULL);
                page = BufferGetPage(lbuf);
                opaque = BTPageGetOpaque(page);
                while (P_ISDELETED(opaque) || opaque->btpo_next != target)
@@ -2449,7 +2467,8 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
                        CHECK_FOR_INTERRUPTS();
 
                        /* step right one page */
-                       lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
+                       lbuf = _bt_getbuf(rel, leftsib, BT_WRITE, &hit);
+                       pgstat_count_buffer(rel, !P_ISLEAF(opaque), hit);
                        page = BufferGetPage(lbuf);
                        opaque = BTPageGetOpaque(page);
                }
@@ -2513,7 +2532,8 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
         * And next write-lock the (current) right sibling.
         */
        rightsib = opaque->btpo_next;
-       rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
+       rbuf = _bt_getbuf(rel, rightsib, BT_WRITE, &hit);
+       pgstat_count_buffer(rel, !P_ISLEAF(opaque), hit);
        page = BufferGetPage(rbuf);
        opaque = BTPageGetOpaque(page);
 
@@ -2569,7 +2589,8 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, 
BlockNumber scanblkno,
                if (P_RIGHTMOST(opaque))
                {
                        /* rightsib will be the only one left on the level */
-                       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
+                       metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE, 
&hit);
+                       pgstat_count_metadata_buffer(rel, hit);
                        metapg = BufferGetPage(metabuf);
                        metad = BTPageGetMeta(metapg);
 
diff --git a/src/backend/access/nbtree/nbtree.c 
b/src/backend/access/nbtree/nbtree.c
index dc244ae24c..7aa24e1f12 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -1146,7 +1146,7 @@ backtrack:
         * buffer access strategy.
         */
        buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
-                                                        info->strategy);
+                                                        info->strategy, NULL);
        _bt_lockbuf(rel, buf, BT_READ);
        page = BufferGetPage(buf);
        opaque = NULL;
diff --git a/src/backend/access/nbtree/nbtsearch.c 
b/src/backend/access/nbtree/nbtsearch.c
index 472ce06f19..203816c418 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -126,6 +126,7 @@ _bt_search(Relation rel, Relation heaprel, BTScanInsert 
key, Buffer *bufP,
                IndexTuple      itup;
                BlockNumber child;
                BTStack         new_stack;
+               bool        hit;
 
                /*
                 * Race -- the page we just grabbed may have split since we 
read its
@@ -178,7 +179,8 @@ _bt_search(Relation rel, Relation heaprel, BTScanInsert 
key, Buffer *bufP,
                        page_access = BT_WRITE;
 
                /* drop the read lock on the page, then acquire one on its 
child */
-               *bufP = _bt_relandgetbuf(rel, *bufP, child, page_access);
+               *bufP = _bt_relandgetbuf(rel, *bufP, child, page_access, &hit);
+               pgstat_count_buffer(rel, opaque->btpo_level != 1, hit);
 
                /* okay, all set to move down a level */
                stack_in = new_stack;
@@ -249,6 +251,7 @@ _bt_moveright(Relation rel,
        Page            page;
        BTPageOpaque opaque;
        int32           cmpval;
+       bool        hit;
 
        Assert(!forupdate || heaprel != NULL);
 
@@ -299,14 +302,16 @@ _bt_moveright(Relation rel,
                                _bt_relbuf(rel, buf);
 
                        /* re-acquire the lock in the right mode, and re-check 
*/
-                       buf = _bt_getbuf(rel, blkno, access);
+                       buf = _bt_getbuf(rel, blkno, access, &hit);
+                       pgstat_count_buffer(rel, !P_ISLEAF(opaque), hit);
                        continue;
                }
 
                if (P_IGNORE(opaque) || _bt_compare(rel, key, page, P_HIKEY) >= 
cmpval)
                {
                        /* step right one page */
-                       buf = _bt_relandgetbuf(rel, buf, opaque->btpo_next, 
access);
+                       buf = _bt_relandgetbuf(rel, buf, opaque->btpo_next, 
access, &hit);
+                       pgstat_count_buffer(rel, !P_ISLEAF(opaque), hit);
                        continue;
                }
                else
@@ -2200,6 +2205,8 @@ static bool
 _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
                                 BlockNumber lastcurrblkno, ScanDirection dir, 
bool seized)
 {
+       bool hit;
+
        Relation        rel = scan->indexRelation;
        BTScanOpaque so = (BTScanOpaque) scan->opaque;
 
@@ -2246,7 +2253,8 @@ _bt_readnextpage(IndexScanDesc scan, BlockNumber blkno,
                {
                        /* read blkno, but check for interrupts first */
                        CHECK_FOR_INTERRUPTS();
-                       so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ);
+                       so->currPos.buf = _bt_getbuf(rel, blkno, BT_READ, &hit);
+                       pgstat_count_record_buffer(rel, hit);
                }
                else
                {
@@ -2342,10 +2350,11 @@ _bt_lock_and_validate_left(Relation rel, BlockNumber 
*blkno,
                Page            page;
                BTPageOpaque opaque;
                int                     tries;
+               bool        hit;
 
                /* check for interrupts while we're not holding any buffer lock 
*/
                CHECK_FOR_INTERRUPTS();
-               buf = _bt_getbuf(rel, *blkno, BT_READ);
+               buf = _bt_getbuf(rel, *blkno, BT_READ, NULL);
                page = BufferGetPage(buf);
                opaque = BTPageGetOpaque(page);
 
@@ -2372,7 +2381,8 @@ _bt_lock_and_validate_left(Relation rel, BlockNumber 
*blkno,
                                break;
                        /* step right */
                        *blkno = opaque->btpo_next;
-                       buf = _bt_relandgetbuf(rel, buf, *blkno, BT_READ);
+                       buf = _bt_relandgetbuf(rel, buf, *blkno, BT_READ, &hit);
+                       pgstat_count_buffer(rel, P_ISLEAF(opaque), hit);
                        page = BufferGetPage(buf);
                        opaque = BTPageGetOpaque(page);
                }
@@ -2382,7 +2392,7 @@ _bt_lock_and_validate_left(Relation rel, BlockNumber 
*blkno,
                 * _bt_readpage, which is passed by caller as lastcurrblkno) to 
see
                 * what's up with its prev sibling link
                 */
-               buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, BT_READ);
+               buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, BT_READ, NULL);
                page = BufferGetPage(buf);
                opaque = BTPageGetOpaque(page);
                if (P_ISDELETED(opaque))
@@ -2399,7 +2409,8 @@ _bt_lock_and_validate_left(Relation rel, BlockNumber 
*blkno,
                                        elog(ERROR, "fell off the end of index 
\"%s\"",
                                                 RelationGetRelationName(rel));
                                lastcurrblkno = opaque->btpo_next;
-                               buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, 
BT_READ);
+                               buf = _bt_relandgetbuf(rel, buf, lastcurrblkno, 
BT_READ, &hit);
+                               pgstat_count_buffer(rel, !P_ISLEAF(opaque), 
hit);
                                page = BufferGetPage(buf);
                                opaque = BTPageGetOpaque(page);
                                if (!P_ISDELETED(opaque))
@@ -2456,6 +2467,7 @@ _bt_get_endpoint(Relation rel, uint32 level, bool 
rightmost)
        OffsetNumber offnum;
        BlockNumber blkno;
        IndexTuple      itup;
+       bool        hit;
 
        /*
         * If we are looking for a leaf page, okay to descend from fast root;
@@ -2488,7 +2500,8 @@ _bt_get_endpoint(Relation rel, uint32 level, bool 
rightmost)
                        if (blkno == P_NONE)
                                elog(ERROR, "fell off the end of index \"%s\"",
                                         RelationGetRelationName(rel));
-                       buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ);
+                       buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ, &hit);
+                       pgstat_count_record_buffer(rel, hit);
                        page = BufferGetPage(buf);
                        opaque = BTPageGetOpaque(page);
                }
@@ -2511,7 +2524,8 @@ _bt_get_endpoint(Relation rel, uint32 level, bool 
rightmost)
                itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, 
offnum));
                blkno = BTreeTupleGetDownLink(itup);
 
-               buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ);
+               buf = _bt_relandgetbuf(rel, buf, blkno, BT_READ, &hit);
+               pgstat_count_record_buffer(rel, hit);
                page = BufferGetPage(buf);
                opaque = BTPageGetOpaque(page);
        }
diff --git a/src/backend/access/nbtree/nbtutils.c 
b/src/backend/access/nbtree/nbtutils.c
index 693e43c674..a550faa84e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -2368,8 +2368,9 @@ _bt_killitems(IndexScanDesc scan)
                Buffer          buf;
 
                droppedpin = true;
-               /* Attempt to re-read the buffer, getting pin and lock. */
-               buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, 
BT_READ);
+               /* Attempt to re-read the buffer, getting pin andlock. */
+               buf = _bt_getbuf(scan->indexRelation, so->currPos.currPage, 
BT_READ,
+                                                NULL);
 
                page = BufferGetPage(buf);
                if (BufferGetLSNAtomic(buf) == so->currPos.lsn)
diff --git a/src/backend/access/spgist/spgdoinsert.c 
b/src/backend/access/spgist/spgdoinsert.c
index af6b27b213..96ca5a91a2 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -2065,13 +2065,13 @@ spgdoinsert(Relation index, SpGistState *state,
                else if (parent.buffer == InvalidBuffer)
                {
                        /* we hold no parent-page lock, so no deadlock is 
possible */
-                       current.buffer = ReadBuffer(index, current.blkno);
+                       current.buffer = ReadBuffer(index, current.blkno, NULL);
                        LockBuffer(current.buffer, BUFFER_LOCK_EXCLUSIVE);
                }
                else if (current.blkno != parent.blkno)
                {
                        /* descend to a new child page */
-                       current.buffer = ReadBuffer(index, current.blkno);
+                       current.buffer = ReadBuffer(index, current.blkno, NULL);
 
                        /*
                         * Attempt to acquire lock on child page.  We must 
beware of
diff --git a/src/backend/access/spgist/spgscan.c 
b/src/backend/access/spgist/spgscan.c
index 53f910e9d8..85f6e91af0 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -847,13 +847,13 @@ redirect:
 
                        if (buffer == InvalidBuffer)
                        {
-                               buffer = ReadBuffer(index, blkno);
+                               buffer = ReadBuffer(index, blkno, NULL);
                                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                        }
                        else if (blkno != BufferGetBlockNumber(buffer))
                        {
                                UnlockReleaseBuffer(buffer);
-                               buffer = ReadBuffer(index, blkno);
+                               buffer = ReadBuffer(index, blkno, NULL);
                                LockBuffer(buffer, BUFFER_LOCK_SHARE);
                        }
 
diff --git a/src/backend/access/spgist/spgutils.c 
b/src/backend/access/spgist/spgutils.c
index 367c36ef9a..ec0c455e6d 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -267,7 +267,7 @@ spgGetCache(Relation index)
                        Buffer          metabuffer;
                        SpGistMetaPageData *metadata;
 
-                       metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
+                       metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO, 
NULL);
                        LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
 
                        metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
@@ -407,7 +407,7 @@ SpGistNewBuffer(Relation index)
                if (SpGistBlockIsFixed(blkno))
                        continue;
 
-               buffer = ReadBuffer(index, blkno);
+               buffer = ReadBuffer(index, blkno, NULL);
 
                /*
                 * We have to guard against the possibility that someone else 
already
@@ -452,7 +452,7 @@ SpGistUpdateMetaPage(Relation index)
        {
                Buffer          metabuffer;
 
-               metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
+               metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO, NULL);
 
                if (ConditionalLockBuffer(metabuffer))
                {
@@ -601,7 +601,7 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, 
bool *isNew)
                Buffer          buffer;
                Page            page;
 
-               buffer = ReadBuffer(index, lup->blkno);
+               buffer = ReadBuffer(index, lup->blkno, NULL);
 
                if (!ConditionalLockBuffer(buffer))
                {
diff --git a/src/backend/access/spgist/spgvacuum.c 
b/src/backend/access/spgist/spgvacuum.c
index eeddacd0d5..b4cf3470b6 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -628,7 +628,7 @@ spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
        vacuum_delay_point(false);
 
        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                               RBM_NORMAL, 
bds->info->strategy);
+                                                               RBM_NORMAL, 
bds->info->strategy, NULL);
        LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
        page = (Page) BufferGetPage(buffer);
 
@@ -709,7 +709,7 @@ spgprocesspending(spgBulkDeleteState *bds)
                /* examine the referenced page */
                blkno = ItemPointerGetBlockNumber(&pitem->tid);
                buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
-                                                                       
RBM_NORMAL, bds->info->strategy);
+                                                                       
RBM_NORMAL, bds->info->strategy, NULL);
                LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
                page = (Page) BufferGetPage(buffer);
 
diff --git a/src/backend/access/transam/xloginsert.c 
b/src/backend/access/transam/xloginsert.c
index 14d583ae7a..8efecc6e19 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -1300,7 +1300,7 @@ log_newpage_range(Relation rel, ForkNumber forknum,
                while (nbufs < XLR_MAX_BLOCK_ID && blkno < endblk)
                {
                        Buffer          buf = ReadBufferExtended(rel, forknum, 
blkno,
-                                                                               
                 RBM_NORMAL, NULL);
+                                                                               
                 RBM_NORMAL, NULL, NULL);
 
                        LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
 
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index eff0990957..7567e4987f 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -758,6 +758,10 @@ CREATE VIEW pg_statio_all_tables AS
             pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit,
             I.idx_blks_read AS idx_blks_read,
             I.idx_blks_hit AS idx_blks_hit,
+            I.idx_metadata_blks_read AS idx_metadata_blks_read,
+            I.idx_metadata_blks_hit AS idx_metadata_blks_hit,
+            I.idx_record_blks_read AS idx_record_blks_read,
+            I.idx_record_blks_hit AS idx_record_blks_hit,
             pg_stat_get_blocks_fetched(T.oid) -
                     pg_stat_get_blocks_hit(T.oid) AS toast_blks_read,
             pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit,
@@ -771,7 +775,17 @@ CREATE VIEW pg_statio_all_tables AS
                          pg_stat_get_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
                      sum(pg_stat_get_blocks_hit(indexrelid))::bigint
-                     AS idx_blks_hit
+                     AS idx_blks_hit,
+                     sum(pg_stat_get_metadata_blocks_fetched(indexrelid) -
+                         pg_stat_get_metadata_blocks_hit(indexrelid))::bigint
+                     AS idx_metadata_blks_read,
+                     sum(pg_stat_get_metadata_blocks_hit(indexrelid))::bigint
+                     AS idx_metadata_blks_hit,
+                     sum(pg_stat_get_record_blocks_fetched(indexrelid) -
+                         pg_stat_get_record_blocks_hit(indexrelid))::bigint
+                     AS idx_record_blks_read,
+                     sum(pg_stat_get_record_blocks_hit(indexrelid))::bigint
+                     AS idx_record_blks_hit
               FROM pg_index WHERE indrelid = C.oid ) I ON true
             LEFT JOIN LATERAL (
               SELECT sum(pg_stat_get_blocks_fetched(indexrelid) -
@@ -828,7 +842,13 @@ CREATE VIEW pg_statio_all_indexes AS
             I.relname AS indexrelname,
             pg_stat_get_blocks_fetched(I.oid) -
                     pg_stat_get_blocks_hit(I.oid) AS idx_blks_read,
-            pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit
+            pg_stat_get_blocks_hit(I.oid) AS idx_blks_hit,
+            pg_stat_get_metadata_blocks_fetched(I.oid) -
+                    pg_stat_get_metadata_blocks_hit(I.oid) AS 
idx_metadata_blks_read,
+            pg_stat_get_metadata_blocks_hit(I.oid) AS idx_metadata_blks_hit,
+            pg_stat_get_record_blocks_fetched(I.oid) -
+                    pg_stat_get_record_blocks_hit(I.oid) AS 
idx_record_blks_read,
+            pg_stat_get_record_blocks_hit(I.oid) AS idx_record_blks_hit
     FROM pg_class C JOIN
             pg_index X ON C.oid = X.indrelid JOIN
             pg_class I ON I.oid = X.indexrelid
@@ -1062,6 +1082,12 @@ CREATE VIEW pg_stat_database AS
             pg_stat_get_db_blocks_fetched(D.oid) -
                     pg_stat_get_db_blocks_hit(D.oid) AS blks_read,
             pg_stat_get_db_blocks_hit(D.oid) AS blks_hit,
+            pg_stat_get_db_metadata_blocks_fetched(D.oid) -
+                    pg_stat_get_db_metadata_blocks_hit(D.oid) AS 
metadata_blks_read,
+            pg_stat_get_db_metadata_blocks_hit(D.oid) AS metadata_blks_hit,
+            pg_stat_get_db_record_blocks_fetched(D.oid) -
+                    pg_stat_get_db_record_blocks_hit(D.oid) AS 
record_blks_read,
+            pg_stat_get_db_record_blocks_hit(D.oid) AS record_blks_hit,
             pg_stat_get_db_tuples_returned(D.oid) AS tup_returned,
             pg_stat_get_db_tuples_fetched(D.oid) AS tup_fetched,
             pg_stat_get_db_tuples_inserted(D.oid) AS tup_inserted,
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 4b7c5113aa..270c9bf826 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1194,7 +1194,7 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple 
seqdatatuple)
        sequence_magic *sm;
        Form_pg_sequence_data seq;
 
-       *buf = ReadBuffer(rel, 0);
+       *buf = ReadBuffer(rel, 0, NULL);
        LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
 
        page = BufferGetPage(*buf);
diff --git a/src/backend/storage/aio/read_stream.c 
b/src/backend/storage/aio/read_stream.c
index 99e44ed99f..03fbc85eac 100644
--- a/src/backend/storage/aio/read_stream.c
+++ b/src/backend/storage/aio/read_stream.c
@@ -267,7 +267,8 @@ read_stream_start_pending_read(ReadStream *stream, bool 
suppress_advice)
                                                                 
&stream->buffers[buffer_index],
                                                                 
stream->pending_read_blocknum,
                                                                 &nblocks,
-                                                                flags);
+                                                                flags,
+                                                                NULL);
        stream->pinned_buffers += nblocks;
 
        /* Remember whether we need to wait before returning this buffer. */
@@ -659,7 +660,8 @@ read_stream_next_buffer(ReadStream *stream, void 
**per_buffer_data)
                                                                                
&stream->buffers[oldest_buffer_index],
                                                                                
next_blocknum,
                                                                                
stream->advice_enabled ?
-                                                                               
READ_BUFFERS_ISSUE_ADVICE : 0)))
+                                                                               
READ_BUFFERS_ISSUE_ADVICE : 0,
+                                                                               
NULL)))
                        {
                                /* Fast return. */
                                return buffer;
diff --git a/src/backend/storage/buffer/bufmgr.c 
b/src/backend/storage/buffer/bufmgr.c
index 75cfc2b6fe..6acb8089a5 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -486,7 +486,8 @@ ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref)
 static Buffer ReadBuffer_common(Relation rel,
                                                                SMgrRelation 
smgr, char smgr_persistence,
                                                                ForkNumber 
forkNum, BlockNumber blockNum,
-                                                               ReadBufferMode 
mode, BufferAccessStrategy strategy);
+                                                               ReadBufferMode 
mode, BufferAccessStrategy strategy,
+                                                               bool *hit);
 static BlockNumber ExtendBufferedRelCommon(BufferManagerRelation bmr,
                                                                                
   ForkNumber fork,
                                                                                
   BufferAccessStrategy strategy,
@@ -743,9 +744,9 @@ ReadRecentBuffer(RelFileLocator rlocator, ForkNumber 
forkNum, BlockNumber blockN
  *             fork with RBM_NORMAL mode and default strategy.
  */
 Buffer
-ReadBuffer(Relation reln, BlockNumber blockNum)
+ReadBuffer(Relation reln, BlockNumber blockNum, bool *hit)
 {
-       return ReadBufferExtended(reln, MAIN_FORKNUM, blockNum, RBM_NORMAL, 
NULL);
+       return ReadBufferExtended(reln, MAIN_FORKNUM, blockNum, RBM_NORMAL, 
NULL, hit);
 }
 
 /*
@@ -791,7 +792,7 @@ ReadBuffer(Relation reln, BlockNumber blockNum)
  */
 inline Buffer
 ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
-                                  ReadBufferMode mode, BufferAccessStrategy 
strategy)
+                                  ReadBufferMode mode, BufferAccessStrategy 
strategy, bool *hit)
 {
        Buffer          buf;
 
@@ -810,7 +811,7 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, 
BlockNumber blockNum,
         * miss.
         */
        buf = ReadBuffer_common(reln, RelationGetSmgr(reln), 0,
-                                                       forkNum, blockNum, 
mode, strategy);
+                                                       forkNum, blockNum, 
mode, strategy, hit);
 
        return buf;
 }
@@ -836,7 +837,7 @@ ReadBufferWithoutRelcache(RelFileLocator rlocator, 
ForkNumber forkNum,
        return ReadBuffer_common(NULL, smgr,
                                                         permanent ? 
RELPERSISTENCE_PERMANENT : RELPERSISTENCE_UNLOGGED,
                                                         forkNum, blockNum,
-                                                        mode, strategy);
+                                                        mode, strategy, NULL);
 }
 
 /*
@@ -1004,7 +1005,7 @@ ExtendBufferedRelTo(BufferManagerRelation bmr,
        {
                Assert(extended_by == 0);
                buffer = ReadBuffer_common(bmr.rel, bmr.smgr, 
bmr.relpersistence,
-                                                                  fork, 
extend_to - 1, mode, strategy);
+                                                                  fork, 
extend_to - 1, mode, strategy, NULL);
        }
 
        return buffer;
@@ -1109,7 +1110,8 @@ PinBufferForBlock(Relation rel,
                                  ForkNumber forkNum,
                                  BlockNumber blockNum,
                                  BufferAccessStrategy strategy,
-                                 bool *foundPtr)
+                                 bool *foundPtr,
+                                 bool *hit)
 {
        BufferDesc *bufHdr;
        IOContext       io_context;
@@ -1160,8 +1162,11 @@ PinBufferForBlock(Relation rel,
                 * zeroed instead), the per-relation stats always count them.
                 */
                pgstat_count_buffer_read(rel);
-               if (*foundPtr)
+               if (*foundPtr) {
+                       if (hit)
+                               *hit = true;
                        pgstat_count_buffer_hit(rel);
+               }
        }
        if (*foundPtr)
        {
@@ -1189,7 +1194,8 @@ static pg_attribute_always_inline Buffer
 ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
                                  ForkNumber forkNum,
                                  BlockNumber blockNum, ReadBufferMode mode,
-                                 BufferAccessStrategy strategy)
+                                 BufferAccessStrategy strategy,
+                                 bool *hit)
 {
        ReadBuffersOperation operation;
        Buffer          buffer;
@@ -1227,7 +1233,7 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char 
smgr_persistence,
                bool            found;
 
                buffer = PinBufferForBlock(rel, smgr, persistence,
-                                                                  forkNum, 
blockNum, strategy, &found);
+                                                                  forkNum, 
blockNum, strategy, &found, hit);
                ZeroAndLockBuffer(buffer, mode, found);
                return buffer;
        }
@@ -1244,7 +1250,8 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char 
smgr_persistence,
        if (StartReadBuffer(&operation,
                                                &buffer,
                                                blockNum,
-                                               flags))
+                                               flags,
+                                               hit))
                WaitReadBuffers(&operation);
 
        return buffer;
@@ -1255,7 +1262,8 @@ StartReadBuffersImpl(ReadBuffersOperation *operation,
                                         Buffer *buffers,
                                         BlockNumber blockNum,
                                         int *nblocks,
-                                        int flags)
+                                        int flags,
+                                        bool *hit)
 {
        int                     actual_nblocks = *nblocks;
        int                     io_buffers_len = 0;
@@ -1274,7 +1282,8 @@ StartReadBuffersImpl(ReadBuffersOperation *operation,
                                                                           
operation->forknum,
                                                                           
blockNum + i,
                                                                           
operation->strategy,
-                                                                          
&found);
+                                                                          
&found,
+                                                                          hit);
 
                if (found)
                {
@@ -1365,9 +1374,10 @@ StartReadBuffers(ReadBuffersOperation *operation,
                                 Buffer *buffers,
                                 BlockNumber blockNum,
                                 int *nblocks,
-                                int flags)
+                                int flags,
+                                bool *hit)
 {
-       return StartReadBuffersImpl(operation, buffers, blockNum, nblocks, 
flags);
+       return StartReadBuffersImpl(operation, buffers, blockNum, nblocks, 
flags, hit);
 }
 
 /*
@@ -1379,12 +1389,13 @@ bool
 StartReadBuffer(ReadBuffersOperation *operation,
                                Buffer *buffer,
                                BlockNumber blocknum,
-                               int flags)
+                               int flags,
+                       bool *hit)
 {
        int                     nblocks = 1;
        bool            result;
 
-       result = StartReadBuffersImpl(operation, buffer, blocknum, &nblocks, 
flags);
+       result = StartReadBuffersImpl(operation, buffer, blocknum, &nblocks, 
flags, hit);
        Assert(nblocks == 1);           /* single block can't be short */
 
        return result;
@@ -2590,7 +2601,8 @@ MarkBufferDirty(Buffer buffer)
 Buffer
 ReleaseAndReadBuffer(Buffer buffer,
                                         Relation relation,
-                                        BlockNumber blockNum)
+                                        BlockNumber blockNum,
+                                        bool *hit)
 {
        ForkNumber      forkNum = MAIN_FORKNUM;
        BufferDesc *bufHdr;
@@ -2619,7 +2631,7 @@ ReleaseAndReadBuffer(Buffer buffer,
                }
        }
 
-       return ReadBuffer(relation, blockNum);
+       return ReadBuffer(relation, blockNum, hit);
 }
 
 /*
diff --git a/src/backend/storage/freespace/freespace.c 
b/src/backend/storage/freespace/freespace.c
index 4773a9cc65..f9f6259ad4 100644
--- a/src/backend/storage/freespace/freespace.c
+++ b/src/backend/storage/freespace/freespace.c
@@ -593,7 +593,7 @@ fsm_readbuf(Relation rel, FSMAddress addr, bool extend)
                        return InvalidBuffer;
        }
        else
-               buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, 
RBM_ZERO_ON_ERROR, NULL);
+               buf = ReadBufferExtended(rel, FSM_FORKNUM, blkno, 
RBM_ZERO_ON_ERROR, NULL, NULL);
 
        /*
         * Initializing the page when needed is trickier than it looks, because 
of
diff --git a/src/backend/utils/activity/pgstat_database.c 
b/src/backend/utils/activity/pgstat_database.c
index 05a8ccfdb7..bbfd75715d 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -407,6 +407,10 @@ pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        PGSTAT_ACCUM_DBCOUNT(xact_rollback);
        PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
        PGSTAT_ACCUM_DBCOUNT(blocks_hit);
+       PGSTAT_ACCUM_DBCOUNT(metadata_blocks_fetched);
+       PGSTAT_ACCUM_DBCOUNT(metadata_blocks_hit);
+       PGSTAT_ACCUM_DBCOUNT(record_blocks_fetched);
+       PGSTAT_ACCUM_DBCOUNT(record_blocks_hit);
 
        PGSTAT_ACCUM_DBCOUNT(tuples_returned);
        PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
diff --git a/src/backend/utils/activity/pgstat_relation.c 
b/src/backend/utils/activity/pgstat_relation.c
index d64595a165..c879c3b2a6 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -871,6 +871,10 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        tabentry->ins_since_vacuum += lstats->counts.tuples_inserted;
        tabentry->blocks_fetched += lstats->counts.blocks_fetched;
        tabentry->blocks_hit += lstats->counts.blocks_hit;
+       tabentry->metadata_blocks_fetched += 
lstats->counts.metadata_blocks_fetched;
+       tabentry->metadata_blocks_hit += lstats->counts.metadata_blocks_hit;
+       tabentry->record_blocks_fetched += lstats->counts.record_blocks_fetched;
+       tabentry->record_blocks_hit += lstats->counts.record_blocks_hit;
 
        /* Clamp live_tuples in case of negative delta_live_tuples */
        tabentry->live_tuples = Max(tabentry->live_tuples, 0);
@@ -888,6 +892,10 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        dbentry->tuples_deleted += lstats->counts.tuples_deleted;
        dbentry->blocks_fetched += lstats->counts.blocks_fetched;
        dbentry->blocks_hit += lstats->counts.blocks_hit;
+       dbentry->metadata_blocks_fetched += 
lstats->counts.metadata_blocks_fetched;
+       dbentry->metadata_blocks_hit += lstats->counts.metadata_blocks_hit;
+       dbentry->record_blocks_fetched += lstats->counts.record_blocks_fetched;
+       dbentry->record_blocks_hit += lstats->counts.record_blocks_hit;
 
        return true;
 }
diff --git a/src/backend/utils/adt/pgstatfuncs.c 
b/src/backend/utils/adt/pgstatfuncs.c
index e9096a8849..565a96773d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -67,6 +67,18 @@ PG_STAT_GET_RELENTRY_INT64(blocks_fetched)
 /* pg_stat_get_blocks_hit */
 PG_STAT_GET_RELENTRY_INT64(blocks_hit)
 
+/* pg_stat_get_metadata_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(metadata_blocks_fetched)
+
+/* pg_stat_get_metadata_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(metadata_blocks_hit)
+
+/* pg_stat_get_record_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(record_blocks_fetched)
+
+/* pg_stat_get_record_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(record_blocks_hit)
+
 /* pg_stat_get_dead_tuples */
 PG_STAT_GET_RELENTRY_INT64(dead_tuples)
 
@@ -1031,6 +1043,18 @@ PG_STAT_GET_DBENTRY_INT64(blocks_fetched)
 /* pg_stat_get_db_blocks_hit */
 PG_STAT_GET_DBENTRY_INT64(blocks_hit)
 
+/* pg_stat_get_db_metadata_blocks_fetched */
+PG_STAT_GET_DBENTRY_INT64(metadata_blocks_fetched)
+
+/* pg_stat_get_db_metadata_blocks_hit */
+PG_STAT_GET_DBENTRY_INT64(metadata_blocks_hit)
+
+/* pg_stat_get_db_record_blocks_fetched */
+PG_STAT_GET_DBENTRY_INT64(record_blocks_fetched)
+
+/* pg_stat_get_db_record_blocks_hit */
+PG_STAT_GET_DBENTRY_INT64(record_blocks_hit)
+
 /* pg_stat_get_db_conflict_bufferpin */
 PG_STAT_GET_DBENTRY_INT64(conflict_bufferpin)
 
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 000c7289b8..9d7e2a6e45 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1247,10 +1247,10 @@ extern int      _bt_getrootheight(Relation rel);
 extern void _bt_metaversion(Relation rel, bool *heapkeyspace,
                                                        bool *allequalimage);
 extern void _bt_checkpage(Relation rel, Buffer buf);
-extern Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access);
+extern Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access, bool 
*hit);
 extern Buffer _bt_allocbuf(Relation rel, Relation heaprel);
 extern Buffer _bt_relandgetbuf(Relation rel, Buffer obuf,
-                                                          BlockNumber blkno, 
int access);
+                                                          BlockNumber blkno, 
int access, bool *hit);
 extern void _bt_relbuf(Relation rel, Buffer buf);
 extern void _bt_lockbuf(Relation rel, Buffer buf, int access);
 extern void _bt_unlockbuf(Relation rel, Buffer buf);
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 9e803d610d..5902827510 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5517,6 +5517,22 @@
   proname => 'pg_stat_get_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_blocks_hit' },
+  { oid => '8888', descr => 'statistics: number of record blocks fetched',
+  proname => 'pg_stat_get_record_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_record_blocks_fetched' },
+{ oid => '8889', descr => 'statistics: number of record blocks found in cache',
+  proname => 'pg_stat_get_record_blocks_hit', provolatile => 's', proparallel 
=> 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_record_blocks_hit' },
+{ oid => '8890', descr => 'statistics: number of metadata blocks fetched',
+  proname => 'pg_stat_get_metadata_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_metadata_blocks_fetched' },
+{ oid => '8891', descr => 'statistics: number of metadata blocks found in 
cache',
+  proname => 'pg_stat_get_metadata_blocks_hit', provolatile => 's', 
proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_metadata_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
   proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
@@ -5717,6 +5733,22 @@
   proname => 'pg_stat_get_db_blocks_hit', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_db_blocks_hit' },
+{ oid => '8892', descr => 'statistics: number of db record blocks fetched',
+  proname => 'pg_stat_get_db_record_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_record_blocks_fetched' },
+{ oid => '8893', descr => 'statistics: blocks found in cache for database',
+  proname => 'pg_stat_get_db_record_blocks_hit', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_record_blocks_hit' },
+{ oid => '8894', descr => 'statistics: number of metadata blocks fetched',
+  proname => 'pg_stat_get_db_metadata_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_metadata_blocks_fetched' },
+{ oid => '8895', descr => 'statistics: number of metadata blocks found in 
cache',
+  proname => 'pg_stat_get_db_metadata_blocks_hit', provolatile => 's', 
proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_db_metadata_blocks_hit' },
 { oid => '2758', descr => 'statistics: tuples returned for database',
   proname => 'pg_stat_get_db_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 53f2a8458e..b846ef7529 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -154,6 +154,11 @@ typedef struct PgStat_TableCounts
 
        PgStat_Counter blocks_fetched;
        PgStat_Counter blocks_hit;
+
+       PgStat_Counter metadata_blocks_fetched;
+       PgStat_Counter metadata_blocks_hit;
+       PgStat_Counter record_blocks_fetched;
+       PgStat_Counter record_blocks_hit;
 } PgStat_TableCounts;
 
 /* ----------
@@ -364,6 +369,10 @@ typedef struct PgStat_StatDBEntry
        PgStat_Counter xact_rollback;
        PgStat_Counter blocks_fetched;
        PgStat_Counter blocks_hit;
+       PgStat_Counter metadata_blocks_fetched;
+       PgStat_Counter metadata_blocks_hit;
+       PgStat_Counter record_blocks_fetched;
+       PgStat_Counter record_blocks_hit;
        PgStat_Counter tuples_returned;
        PgStat_Counter tuples_fetched;
        PgStat_Counter tuples_inserted;
@@ -459,6 +468,11 @@ typedef struct PgStat_StatTabEntry
        PgStat_Counter blocks_fetched;
        PgStat_Counter blocks_hit;
 
+       PgStat_Counter metadata_blocks_fetched;
+       PgStat_Counter metadata_blocks_hit;
+       PgStat_Counter record_blocks_fetched;
+       PgStat_Counter record_blocks_hit;
+
        TimestampTz last_vacuum_time;   /* user initiated vacuum */
        PgStat_Counter vacuum_count;
        TimestampTz last_autovacuum_time;       /* autovacuum initiated */
@@ -707,6 +721,37 @@ extern void pgstat_report_analyze(Relation rel,
                if (pgstat_should_count_relation(rel))                          
                \
                        (rel)->pgstat_info->counts.blocks_hit++;                
                \
        } while (0)
+#define pgstat_count_metadata_buffer(rel, hit)                                 
    \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_relation(rel)) {                    \
+                       (rel)->pgstat_info->counts.metadata_blocks_fetched++;   
\
+                       if ((hit))                                              
                                                \
+                               
(rel)->pgstat_info->counts.metadata_blocks_hit++;   \
+               }                                                               
                                            \
+       } while (0)
+#define pgstat_count_record_buffer(rel, hit)                                   
    \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_relation(rel)) {                    \
+                       (rel)->pgstat_info->counts.record_blocks_fetched++;     
\
+                       if ((hit))                                              
                                                \
+                               (rel)->pgstat_info->counts.record_blocks_hit++; 
    \
+               }                                                               
                                            \
+       } while (0)
+#define pgstat_count_buffer(rel, metadata, hit)                                
            \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_relation(rel)) {                    \
+                       if ((metadata)) {                                       
                                        \
+                               
(rel)->pgstat_info->counts.metadata_blocks_fetched++;\
+                               if ((hit))                                      
    \
+                                       
(rel)->pgstat_info->counts.metadata_blocks_hit++;\
+                       }                                                       
                                                        \
+                       else {                                                  
                                                \
+                               
(rel)->pgstat_info->counts.record_blocks_fetched++; \
+                               if ((hit))                                      
                                            \
+                                  
(rel)->pgstat_info->counts.record_blocks_hit++;  \
+                       }                                                       
                                                        \
+          }                                                                    
                                                \
+       } while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
 extern void pgstat_count_heap_update(Relation rel, bool hot, bool newpage);
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index 7c1e4316dd..8f287f0841 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -201,10 +201,10 @@ extern PrefetchBufferResult PrefetchBuffer(Relation reln, 
ForkNumber forkNum,
                                                                                
   BlockNumber blockNum);
 extern bool ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum,
                                                         BlockNumber blockNum, 
Buffer recent_buffer);
-extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum);
+extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum, bool *hit);
 extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum,
                                                                 BlockNumber 
blockNum, ReadBufferMode mode,
-                                                                
BufferAccessStrategy strategy);
+                                                                
BufferAccessStrategy strategy, bool *hit);
 extern Buffer ReadBufferWithoutRelcache(RelFileLocator rlocator,
                                                                                
ForkNumber forkNum, BlockNumber blockNum,
                                                                                
ReadBufferMode mode, BufferAccessStrategy strategy,
@@ -213,12 +213,14 @@ extern Buffer ReadBufferWithoutRelcache(RelFileLocator 
rlocator,
 extern bool StartReadBuffer(ReadBuffersOperation *operation,
                                                        Buffer *buffer,
                                                        BlockNumber blocknum,
-                                                       int flags);
+                                                       int flags,
+                                                       bool *hit);
 extern bool StartReadBuffers(ReadBuffersOperation *operation,
                                                         Buffer *buffers,
                                                         BlockNumber blockNum,
                                                         int *nblocks,
-                                                        int flags);
+                                                        int flags,
+                                                        bool *hit);
 extern void WaitReadBuffers(ReadBuffersOperation *operation);
 
 extern void ReleaseBuffer(Buffer buffer);
@@ -229,7 +231,7 @@ extern void MarkBufferDirty(Buffer buffer);
 extern void IncrBufferRefCount(Buffer buffer);
 extern void CheckBufferIsPinnedOnce(Buffer buffer);
 extern Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation,
-                                                                  BlockNumber 
blockNum);
+                                                                  BlockNumber 
blockNum, bool *hit);
 
 extern Buffer ExtendBufferedRel(BufferManagerRelation bmr,
                                                                ForkNumber 
forkNum,
diff --git a/src/test/regress/expected/rules.out 
b/src/test/regress/expected/rules.out
index 5baba8d39f..5217cc74a9 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1847,6 +1847,10 @@ pg_stat_database| SELECT oid AS datid,
     pg_stat_get_db_xact_rollback(oid) AS xact_rollback,
     (pg_stat_get_db_blocks_fetched(oid) - pg_stat_get_db_blocks_hit(oid)) AS 
blks_read,
     pg_stat_get_db_blocks_hit(oid) AS blks_hit,
+    (pg_stat_get_db_metadata_blocks_fetched(oid) - 
pg_stat_get_db_metadata_blocks_hit(oid)) AS metadata_blks_read,
+    pg_stat_get_db_metadata_blocks_hit(oid) AS metadata_blks_hit,
+    (pg_stat_get_db_record_blocks_fetched(oid) - 
pg_stat_get_db_record_blocks_hit(oid)) AS record_blks_read,
+    pg_stat_get_db_record_blocks_hit(oid) AS record_blks_hit,
     pg_stat_get_db_tuples_returned(oid) AS tup_returned,
     pg_stat_get_db_tuples_fetched(oid) AS tup_fetched,
     pg_stat_get_db_tuples_inserted(oid) AS tup_inserted,
@@ -2342,7 +2346,11 @@ pg_statio_all_indexes| SELECT c.oid AS relid,
     c.relname,
     i.relname AS indexrelname,
     (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS 
idx_blks_read,
-    pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit
+    pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit,
+    (pg_stat_get_metadata_blocks_fetched(i.oid) - 
pg_stat_get_metadata_blocks_hit(i.oid)) AS idx_metadata_blks_read,
+    pg_stat_get_metadata_blocks_hit(i.oid) AS idx_metadata_blks_hit,
+    (pg_stat_get_record_blocks_fetched(i.oid) - 
pg_stat_get_record_blocks_hit(i.oid)) AS idx_record_blks_read,
+    pg_stat_get_record_blocks_hit(i.oid) AS idx_record_blks_hit
    FROM (((pg_class c
      JOIN pg_index x ON ((c.oid = x.indrelid)))
      JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -2363,6 +2371,10 @@ pg_statio_all_tables| SELECT c.oid AS relid,
     pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit,
     i.idx_blks_read,
     i.idx_blks_hit,
+    i.idx_metadata_blks_read,
+    i.idx_metadata_blks_hit,
+    i.idx_record_blks_read,
+    i.idx_record_blks_hit,
     (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS 
toast_blks_read,
     pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit,
     x.idx_blks_read AS tidx_blks_read,
@@ -2371,7 +2383,11 @@ pg_statio_all_tables| SELECT c.oid AS relid,
      LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
      LEFT JOIN LATERAL ( SELECT 
(sum((pg_stat_get_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
-            (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS 
idx_blks_hit
+            (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS 
idx_blks_hit,
+            (sum((pg_stat_get_metadata_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_metadata_blocks_hit(pg_index.indexrelid))))::bigint AS 
idx_metadata_blks_read,
+            
(sum(pg_stat_get_metadata_blocks_hit(pg_index.indexrelid)))::bigint AS 
idx_metadata_blks_hit,
+            (sum((pg_stat_get_record_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_record_blocks_hit(pg_index.indexrelid))))::bigint AS 
idx_record_blks_read,
+            (sum(pg_stat_get_record_blocks_hit(pg_index.indexrelid)))::bigint 
AS idx_record_blks_hit
            FROM pg_index
           WHERE (pg_index.indrelid = c.oid)) i ON (true))
      LEFT JOIN LATERAL ( SELECT 
(sum((pg_stat_get_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
@@ -2385,7 +2401,11 @@ pg_statio_sys_indexes| SELECT relid,
     relname,
     indexrelname,
     idx_blks_read,
-    idx_blks_hit
+    idx_blks_hit,
+    idx_metadata_blks_read,
+    idx_metadata_blks_hit,
+    idx_record_blks_read,
+    idx_record_blks_hit
    FROM pg_statio_all_indexes
   WHERE ((schemaname = ANY (ARRAY['pg_catalog'::name, 
'information_schema'::name])) OR (schemaname ~ '^pg_toast'::text));
 pg_statio_sys_sequences| SELECT relid,
@@ -2402,6 +2422,10 @@ pg_statio_sys_tables| SELECT relid,
     heap_blks_hit,
     idx_blks_read,
     idx_blks_hit,
+    idx_metadata_blks_read,
+    idx_metadata_blks_hit,
+    idx_record_blks_read,
+    idx_record_blks_hit,
     toast_blks_read,
     toast_blks_hit,
     tidx_blks_read,
@@ -2414,7 +2438,11 @@ pg_statio_user_indexes| SELECT relid,
     relname,
     indexrelname,
     idx_blks_read,
-    idx_blks_hit
+    idx_blks_hit,
+    idx_metadata_blks_read,
+    idx_metadata_blks_hit,
+    idx_record_blks_read,
+    idx_record_blks_hit
    FROM pg_statio_all_indexes
   WHERE ((schemaname <> ALL (ARRAY['pg_catalog'::name, 
'information_schema'::name])) AND (schemaname !~ '^pg_toast'::text));
 pg_statio_user_sequences| SELECT relid,
@@ -2431,6 +2459,10 @@ pg_statio_user_tables| SELECT relid,
     heap_blks_hit,
     idx_blks_read,
     idx_blks_hit,
+    idx_metadata_blks_read,
+    idx_metadata_blks_hit,
+    idx_record_blks_read,
+    idx_record_blks_hit,
     toast_blks_read,
     toast_blks_hit,
     tidx_blks_read,
-- 
2.39.5 (Apple Git-154)

Reply via email to