Hi hackers,

Please find attached a patch proposal to split index and table statistics into different types of stats.

This idea has been proposed by Andres in a couple of threads, see [1] and [2].

To sum up:

We currently track index and table types of statistics in the same format (so that a number of the "columns" in index stats are currently unused) and we rename column in views etc to make them somewhat sensible.

So that the immediate benefits to $SUBJECT are:

- have reasonable names for the fields
- shrink the current memory usage

The attached patch proposal:

- renames PGSTAT_KIND_RELATION to PGSTAT_KIND_TABLE
- creates a new PGSTAT_KIND_INDEX
- creates new macros: pgstat_count_index_fetch(), pgstat_count_index_scan(), pgstat_count_index_tuples(), pgstat_count_index_buffer_read() and pgstat_count_index_buffer_hit() to increment the indexes related stats - creates new SQL callable functions dedicated to the indexes that are used in system_views.sql

It also adds basic tests in src/test/regress/sql/stats.sql for toast and partitions (we may want to create a dedicated patch for those additional tests though).

The fields renaming has not been done to ease the reading of this patch (I think it would be better to create a dedicated patch for the renaming once the split is done).

I'm adding a new CF entry for this patch.

Looking forward to your feedback,

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com


[1]: https://www.postgresql.org/message-id/flat/20221019181930.bx73kul4nbiftr65%40awork3.anarazel.de

[2]: https://www.postgresql.org/message-id/20220818195124.c7ipzf6c5v7vxymc%40awork3.anarazel.de
diff --git a/src/backend/access/index/indexam.c 
b/src/backend/access/index/indexam.c
index fe80b8b0ba..a1defc6838 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -582,7 +582,7 @@ index_fetch_heap(IndexScanDesc scan, TupleTableSlot *slot)
                                                                        
&scan->xs_heap_continue, &all_dead);
 
        if (found)
-               pgstat_count_heap_fetch(scan->indexRelation);
+               pgstat_count_index_fetch(scan->indexRelation);
 
        /*
         * If we scanned a whole HOT chain and found only dead tuples, tell 
index
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 5b49cc5a09..8a715db82e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1853,7 +1853,7 @@ heap_drop_with_catalog(Oid relid)
                RelationDropStorage(rel);
 
        /* ensure that stats are dropped if transaction commits */
-       pgstat_drop_relation(rel);
+       pgstat_drop_heap(rel);
 
        /*
         * Close relcache entry, but *keep* AccessExclusiveLock on the relation
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 61f1d3926a..28b94fef7f 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1752,7 +1752,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, 
const char *oldName)
        changeDependenciesOn(RelationRelationId, oldIndexId, newIndexId);
 
        /* copy over statistics from old to new index */
-       pgstat_copy_relation_stats(newClassRel, oldClassRel);
+       pgstat_copy_index_stats(newClassRel, oldClassRel);
 
        /* Copy data of pg_statistic from the old index to the new one */
        CopyStatistics(oldIndexId, newIndexId);
@@ -2326,7 +2326,7 @@ index_drop(Oid indexId, bool concurrent, bool 
concurrent_lock_mode)
                RelationDropStorage(userIndexRelation);
 
        /* ensure that stats are dropped if transaction commits */
-       pgstat_drop_relation(userIndexRelation);
+       pgstat_drop_index(userIndexRelation);
 
        /*
         * Close and flush the index's relcache entry, to ensure relcache 
doesn't
diff --git a/src/backend/catalog/system_views.sql 
b/src/backend/catalog/system_views.sql
index 2d8104b090..e92e50edf7 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,13 +655,13 @@ CREATE VIEW pg_stat_all_tables AS
             C.oid AS relid,
             N.nspname AS schemaname,
             C.relname AS relname,
-            pg_stat_get_numscans(C.oid) AS seq_scan,
-            pg_stat_get_lastscan(C.oid) AS last_seq_scan,
-            pg_stat_get_tuples_returned(C.oid) AS seq_tup_read,
-            sum(pg_stat_get_numscans(I.indexrelid))::bigint AS idx_scan,
-            max(pg_stat_get_lastscan(I.indexrelid)) AS last_idx_scan,
-            sum(pg_stat_get_tuples_fetched(I.indexrelid))::bigint +
-            pg_stat_get_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_heap_numscans(C.oid) AS seq_scan,
+            pg_stat_get_heap_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_heap_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_index_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_index_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_index_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_heap_tuples_fetched(C.oid) AS idx_tup_fetch,
             pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins,
             pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
             pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
@@ -689,9 +689,9 @@ CREATE VIEW pg_stat_xact_all_tables AS
             C.oid AS relid,
             N.nspname AS schemaname,
             C.relname AS relname,
-            pg_stat_get_xact_numscans(C.oid) AS seq_scan,
+            pg_stat_get_heap_xact_numscans(C.oid) AS seq_scan,
             pg_stat_get_xact_tuples_returned(C.oid) AS seq_tup_read,
-            sum(pg_stat_get_xact_numscans(I.indexrelid))::bigint AS idx_scan,
+            sum(pg_stat_get_index_xact_numscans(I.indexrelid))::bigint AS 
idx_scan,
             sum(pg_stat_get_xact_tuples_fetched(I.indexrelid))::bigint +
             pg_stat_get_xact_tuples_fetched(C.oid) AS idx_tup_fetch,
             pg_stat_get_xact_tuples_inserted(C.oid) AS n_tup_ins,
@@ -729,31 +729,31 @@ CREATE VIEW pg_statio_all_tables AS
             C.oid AS relid,
             N.nspname AS schemaname,
             C.relname AS relname,
-            pg_stat_get_blocks_fetched(C.oid) -
-                    pg_stat_get_blocks_hit(C.oid) AS heap_blks_read,
-            pg_stat_get_blocks_hit(C.oid) AS heap_blks_hit,
+            pg_stat_get_heap_blocks_fetched(C.oid) -
+                    pg_stat_get_heap_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_heap_blocks_hit(C.oid) AS heap_blks_hit,
             I.idx_blks_read AS idx_blks_read,
             I.idx_blks_hit AS idx_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,
+            pg_stat_get_heap_blocks_fetched(T.oid) -
+                    pg_stat_get_heap_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_heap_blocks_hit(T.oid) AS toast_blks_hit,
             X.idx_blks_read AS tidx_blks_read,
             X.idx_blks_hit AS tidx_blks_hit
     FROM pg_class C 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(indexrelid) -
-                         pg_stat_get_blocks_hit(indexrelid))::bigint
+              SELECT sum(pg_stat_get_index_blocks_fetched(indexrelid) -
+                         pg_stat_get_index_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_index_blocks_hit(indexrelid))::bigint
                      AS idx_blks_hit
               FROM pg_index WHERE indrelid = C.oid ) I ON true
             LEFT JOIN LATERAL (
-              SELECT sum(pg_stat_get_blocks_fetched(indexrelid) -
-                         pg_stat_get_blocks_hit(indexrelid))::bigint
+              SELECT sum(pg_stat_get_index_blocks_fetched(indexrelid) -
+                         pg_stat_get_index_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_index_blocks_hit(indexrelid))::bigint
                      AS idx_blks_hit
               FROM pg_index WHERE indrelid = T.oid ) X ON true
     WHERE C.relkind IN ('r', 't', 'm');
@@ -775,10 +775,10 @@ CREATE VIEW pg_stat_all_indexes AS
             N.nspname AS schemaname,
             C.relname AS relname,
             I.relname AS indexrelname,
-            pg_stat_get_numscans(I.oid) AS idx_scan,
-            pg_stat_get_lastscan(I.oid) AS last_idx_scan,
-            pg_stat_get_tuples_returned(I.oid) AS idx_tup_read,
-            pg_stat_get_tuples_fetched(I.oid) AS idx_tup_fetch
+            pg_stat_get_index_numscans(I.oid) AS idx_scan,
+            pg_stat_get_index_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_index_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_index_tuples_fetched(I.oid) AS idx_tup_fetch
     FROM pg_class C JOIN
             pg_index X ON C.oid = X.indrelid JOIN
             pg_class I ON I.oid = X.indexrelid
@@ -802,9 +802,9 @@ CREATE VIEW pg_statio_all_indexes AS
             N.nspname AS schemaname,
             C.relname AS 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_index_blocks_fetched(I.oid) -
+                    pg_stat_get_index_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_index_blocks_hit(I.oid) AS idx_blks_hit
     FROM pg_class C JOIN
             pg_index X ON C.oid = X.indrelid JOIN
             pg_class I ON I.oid = X.indexrelid
@@ -826,9 +826,9 @@ CREATE VIEW pg_statio_all_sequences AS
             C.oid AS relid,
             N.nspname AS schemaname,
             C.relname AS relname,
-            pg_stat_get_blocks_fetched(C.oid) -
-                    pg_stat_get_blocks_hit(C.oid) AS blks_read,
-            pg_stat_get_blocks_hit(C.oid) AS blks_hit
+            pg_stat_get_index_blocks_fetched(C.oid) -
+                    pg_stat_get_heap_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_heap_blocks_hit(C.oid) AS blks_hit
     FROM pg_class C
             LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
     WHERE C.relkind = 'S';
diff --git a/src/backend/storage/buffer/bufmgr.c 
b/src/backend/storage/buffer/bufmgr.c
index 73d30bf619..2e3b982aae 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -776,11 +776,19 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, 
BlockNumber blockNum,
         * Read the buffer, and update pgstat counters to reflect a cache hit or
         * miss.
         */
-       pgstat_count_buffer_read(reln);
+       if (reln->rd_rel->relkind == RELKIND_INDEX)
+               pgstat_count_index_buffer_read(reln);
+       else
+               pgstat_count_heap_buffer_read(reln);
        buf = ReadBuffer_common(RelationGetSmgr(reln), 
reln->rd_rel->relpersistence,
                                                        forkNum, blockNum, 
mode, strategy, &hit);
        if (hit)
-               pgstat_count_buffer_hit(reln);
+       {
+               if (reln->rd_rel->relkind == RELKIND_INDEX)
+                       pgstat_count_index_buffer_hit(reln);
+               else
+                       pgstat_count_heap_buffer_hit(reln);
+       }
        return buf;
 }
 
diff --git a/src/backend/utils/activity/pgstat.c 
b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..9349d7f00c 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -269,18 +269,32 @@ static const PgStat_KindInfo 
pgstat_kind_infos[PGSTAT_NUM_KINDS] = {
                .reset_timestamp_cb = pgstat_database_reset_timestamp_cb,
        },
 
-       [PGSTAT_KIND_RELATION] = {
-               .name = "relation",
+       [PGSTAT_KIND_TABLE] = {
+               .name = "table",
 
                .fixed_amount = false,
 
-               .shared_size = sizeof(PgStatShared_Relation),
-               .shared_data_off = offsetof(PgStatShared_Relation, stats),
-               .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
+               .shared_size = sizeof(PgStatShared_Table),
+               .shared_data_off = offsetof(PgStatShared_Table, stats),
+               .shared_data_len = sizeof(((PgStatShared_Table *) 0)->stats),
                .pending_size = sizeof(PgStat_TableStatus),
 
-               .flush_pending_cb = pgstat_relation_flush_cb,
-               .delete_pending_cb = pgstat_relation_delete_pending_cb,
+               .flush_pending_cb = pgstat_table_flush_cb,
+               .delete_pending_cb = pgstat_table_delete_pending_cb,
+       },
+
+       [PGSTAT_KIND_INDEX] = {
+               .name = "index",
+
+               .fixed_amount = false,
+
+               .shared_size = sizeof(PgStatShared_Index),
+               .shared_data_off = offsetof(PgStatShared_Index, stats),
+               .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats),
+               .pending_size = sizeof(PgStat_IndexStatus),
+
+               .flush_pending_cb = pgstat_index_flush_cb,
+               .delete_pending_cb = pgstat_index_delete_pending_cb,
        },
 
        [PGSTAT_KIND_FUNCTION] = {
diff --git a/src/backend/utils/activity/pgstat_relation.c 
b/src/backend/utils/activity/pgstat_relation.c
index 55a355f583..5d30b4c242 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -43,35 +43,35 @@ typedef struct TwoPhasePgStatRecord
 } TwoPhasePgStatRecord;
 
 
-static PgStat_TableStatus *pgstat_prep_relation_pending(Oid rel_id, bool 
isshared);
-static void add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int 
nest_level);
-static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info);
+static PgStat_TableStatus *pgstat_prep_table_pending(Oid rel_id, bool 
isshared);
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool 
isshared);
+static void add_tabstat_xact_level(PgStat_TableStatus *pgstattab_info, int 
nest_level);
+static void ensure_tabstat_xact_level(PgStat_TableStatus *pgstattab_info);
 static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool 
is_drop);
 static void restore_truncdrop_counters(PgStat_TableXactStatus *trans);
 
-
 /*
- * Copy stats between relations. This is used for things like REINDEX
+ * Copy stats between indexes. This is used for things like REINDEX
  * CONCURRENTLY.
  */
 void
-pgstat_copy_relation_stats(Relation dst, Relation src)
+pgstat_copy_index_stats(Relation dst, Relation src)
 {
-       PgStat_StatTabEntry *srcstats;
-       PgStatShared_Relation *dstshstats;
+       PgStat_StatIndEntry *srcstats;
+       PgStatShared_Index *dstshstats;
        PgStat_EntryRef *dst_ref;
 
-       srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
+       srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
                                                                                
          RelationGetRelid(src));
        if (!srcstats)
                return;
 
-       dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+       dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
                                                                                
  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
                                                                                
  RelationGetRelid(dst),
                                                                                
  false);
 
-       dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
+       dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
        dstshstats->stats = *srcstats;
 
        pgstat_unlock_entry(dst_ref);
@@ -81,8 +81,9 @@ pgstat_copy_relation_stats(Relation dst, Relation src)
  * Initialize a relcache entry to count access statistics.  Called whenever a
  * relation is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
- * when the relcache entry is made; thereafter it is long-lived data.
+ * We assume that a relcache entry's pgstattab_info and pgstatind_info fields
+ * are zeroed by relcache.c when the relcache entry is made; thereafter it is
+ * long-lived data.
  *
  * This does not create a reference to a stats entry in shared memory, nor
  * allocate memory for the pending stats. That happens in
@@ -99,18 +100,20 @@ pgstat_init_relation(Relation rel)
        if (!RELKIND_HAS_STORAGE(relkind) && relkind != 
RELKIND_PARTITIONED_TABLE)
        {
                rel->pgstat_enabled = false;
-               rel->pgstat_info = NULL;
+               rel->pgstattab_info = NULL;
+               rel->pgstatind_info = NULL;
                return;
        }
 
        if (!pgstat_track_counts)
        {
-               if (rel->pgstat_info)
+               if (rel->pgstattab_info != NULL || rel->pgstatind_info != NULL)
                        pgstat_unlink_relation(rel);
 
                /* We're not counting at all */
                rel->pgstat_enabled = false;
-               rel->pgstat_info = NULL;
+               rel->pgstattab_info = NULL;
+               rel->pgstatind_info = NULL;
                return;
        }
 
@@ -118,10 +121,10 @@ pgstat_init_relation(Relation rel)
 }
 
 /*
- * Prepare for statistics for this relation to be collected.
+ * Prepare for statistics for this table to be collected.
  *
  * This ensures we have a reference to the stats entry before stats can be
- * generated. That is important because a relation drop in another connection
+ * generated. That is important because a table drop in another connection
  * could otherwise lead to the stats entry being dropped, which then later
  * would get recreated when flushing stats.
  *
@@ -129,20 +132,48 @@ pgstat_init_relation(Relation rel)
  * relcache entries to be opened without ever getting stats reported.
  */
 void
-pgstat_assoc_relation(Relation rel)
+pgstat_assoc_table(Relation rel)
 {
        Assert(rel->pgstat_enabled);
-       Assert(rel->pgstat_info == NULL);
+       Assert(rel->pgstattab_info == NULL);
 
        /* Else find or make the PgStat_TableStatus entry, and update link */
-       rel->pgstat_info = pgstat_prep_relation_pending(RelationGetRelid(rel),
+       rel->pgstattab_info = pgstat_prep_table_pending(RelationGetRelid(rel),
+                                                                               
                        rel->rd_rel->relisshared);
+
+       /* don't allow link a stats to multiple relcache entries */
+       Assert(rel->pgstattab_info->relation == NULL);
+
+       /* mark this relation as the owner */
+       rel->pgstattab_info->relation = rel;
+}
+
+/*
+ * Prepare for statistics for this index to be collected.
+ *
+ * This ensures we have a reference to the stats entry before stats can be
+ * generated. That is important because an index drop in another
+ * connection could otherwise lead to the stats entry being dropped, which then
+ * later would get recreated when flushing stats.
+ *
+ * This is separate from pgstat_init_relation() as it is not uncommon for
+ * relcache entries to be opened without ever getting stats reported.
+ */
+void
+pgstat_assoc_index(Relation rel)
+{
+       Assert(rel->pgstat_enabled);
+       Assert(rel->pgstatind_info == NULL);
+
+       /* Else find or make the PgStat_IndexStatus entry, and update link */
+       rel->pgstatind_info = pgstat_prep_index_pending(RelationGetRelid(rel),
                                                                                
                        rel->rd_rel->relisshared);
 
        /* don't allow link a stats to multiple relcache entries */
-       Assert(rel->pgstat_info->relation == NULL);
+       Assert(rel->pgstatind_info->relation == NULL);
 
        /* mark this relation as the owner */
-       rel->pgstat_info->relation = rel;
+       rel->pgstatind_info->relation = rel;
 }
 
 /*
@@ -152,14 +183,25 @@ pgstat_assoc_relation(Relation rel)
 void
 pgstat_unlink_relation(Relation rel)
 {
-       /* remove the link to stats info if any */
-       if (rel->pgstat_info == NULL)
+
+       if (rel->pgstatind_info == NULL && rel->pgstattab_info == NULL)
                return;
 
-       /* link sanity check */
-       Assert(rel->pgstat_info->relation == rel);
-       rel->pgstat_info->relation = NULL;
-       rel->pgstat_info = NULL;
+       /* link sanity check for the table stats */
+       if (rel->pgstattab_info)
+       {
+               Assert(rel->pgstattab_info->relation == rel);
+               rel->pgstattab_info->relation = NULL;
+               rel->pgstattab_info = NULL;
+       }
+
+       /* link sanity check for the index stats */
+       if (rel->pgstatind_info)
+       {
+               Assert(rel->pgstatind_info->relation == rel);
+               rel->pgstatind_info->relation = NULL;
+               rel->pgstatind_info = NULL;
+       }
 }
 
 /*
@@ -168,39 +210,55 @@ pgstat_unlink_relation(Relation rel)
 void
 pgstat_create_relation(Relation rel)
 {
-       pgstat_create_transactional(PGSTAT_KIND_RELATION,
-                                                               
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-                                                               
RelationGetRelid(rel));
+       if (rel->rd_rel->relkind == RELKIND_INDEX)
+               pgstat_create_transactional(PGSTAT_KIND_INDEX,
+                                                                       
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+                                                                       
RelationGetRelid(rel));
+       else
+               pgstat_create_transactional(PGSTAT_KIND_TABLE,
+                                                                       
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+                                                                       
RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+       pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+                                                         
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+                                                         
RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_heap(Relation rel)
 {
        int                     nest_level = GetCurrentTransactionNestLevel();
-       PgStat_TableStatus *pgstat_info;
+       PgStat_TableStatus *pgstattab_info;
 
-       pgstat_drop_transactional(PGSTAT_KIND_RELATION,
+       pgstat_drop_transactional(PGSTAT_KIND_TABLE,
                                                          
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
                                                          
RelationGetRelid(rel));
 
-       if (!pgstat_should_count_relation(rel))
+       if (!pgstat_should_count_table(rel))
                return;
 
        /*
         * Transactionally set counters to 0. That ensures that accesses to
         * pg_stat_xact_all_tables inside the transaction show 0.
         */
-       pgstat_info = rel->pgstat_info;
-       if (pgstat_info->trans &&
-               pgstat_info->trans->nest_level == nest_level)
+       pgstattab_info = rel->pgstattab_info;
+       if (pgstattab_info->trans &&
+               pgstattab_info->trans->nest_level == nest_level)
        {
-               save_truncdrop_counters(pgstat_info->trans, true);
-               pgstat_info->trans->tuples_inserted = 0;
-               pgstat_info->trans->tuples_updated = 0;
-               pgstat_info->trans->tuples_deleted = 0;
+               save_truncdrop_counters(pgstattab_info->trans, true);
+               pgstattab_info->trans->tuples_inserted = 0;
+               pgstattab_info->trans->tuples_updated = 0;
+               pgstattab_info->trans->tuples_deleted = 0;
        }
 }
 
@@ -212,7 +270,7 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
                                         PgStat_Counter livetuples, 
PgStat_Counter deadtuples)
 {
        PgStat_EntryRef *entry_ref;
-       PgStatShared_Relation *shtabentry;
+       PgStatShared_Table *shtabentry;
        PgStat_StatTabEntry *tabentry;
        Oid                     dboid = (shared ? InvalidOid : MyDatabaseId);
        TimestampTz ts;
@@ -224,10 +282,10 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
        ts = GetCurrentTimestamp();
 
        /* block acquiring lock for the same reason as pgstat_report_autovac() 
*/
-       entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+       entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TABLE,
                                                                                
        dboid, tableoid, false);
 
-       shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+       shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
        tabentry = &shtabentry->stats;
 
        tabentry->n_live_tuples = livetuples;
@@ -271,7 +329,7 @@ pgstat_report_analyze(Relation rel,
                                          bool resetcounter)
 {
        PgStat_EntryRef *entry_ref;
-       PgStatShared_Relation *shtabentry;
+       PgStatShared_Table *shtabentry;
        PgStat_StatTabEntry *tabentry;
        Oid                     dboid = (rel->rd_rel->relisshared ? InvalidOid 
: MyDatabaseId);
 
@@ -290,31 +348,31 @@ pgstat_report_analyze(Relation rel,
         *
         * Waste no time on partitioned tables, though.
         */
-       if (pgstat_should_count_relation(rel) &&
+       if (pgstat_should_count_table(rel) &&
                rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
        {
                PgStat_TableXactStatus *trans;
 
-               for (trans = rel->pgstat_info->trans; trans; trans = 
trans->upper)
+               for (trans = rel->pgstattab_info->trans; trans; trans = 
trans->upper)
                {
                        livetuples -= trans->tuples_inserted - 
trans->tuples_deleted;
                        deadtuples -= trans->tuples_updated + 
trans->tuples_deleted;
                }
                /* count stuff inserted by already-aborted subxacts, too */
-               deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
+               deadtuples -= rel->pgstattab_info->t_counts.t_delta_dead_tuples;
                /* Since ANALYZE's counts are estimates, we could have 
underflowed */
                livetuples = Max(livetuples, 0);
                deadtuples = Max(deadtuples, 0);
        }
 
        /* block acquiring lock for the same reason as pgstat_report_autovac() 
*/
-       entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION, dboid,
+       entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TABLE, dboid,
                                                                                
        RelationGetRelid(rel),
                                                                                
        false);
        /* can't get dropped while accessed */
        Assert(entry_ref != NULL && entry_ref->shared_stats != NULL);
 
-       shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+       shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
        tabentry = &shtabentry->stats;
 
        tabentry->n_live_tuples = livetuples;
@@ -348,12 +406,12 @@ pgstat_report_analyze(Relation rel,
 void
 pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
 {
-       if (pgstat_should_count_relation(rel))
+       if (pgstat_should_count_table(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-               ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_inserted += n;
+               ensure_tabstat_xact_level(pgstattab_info);
+               pgstattab_info->trans->tuples_inserted += n;
        }
 }
 
@@ -363,16 +421,16 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
 void
 pgstat_count_heap_update(Relation rel, bool hot)
 {
-       if (pgstat_should_count_relation(rel))
+       if (pgstat_should_count_table(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-               ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_updated++;
+               ensure_tabstat_xact_level(pgstattab_info);
+               pgstattab_info->trans->tuples_updated++;
 
                /* t_tuples_hot_updated is nontransactional, so just advance it 
*/
                if (hot)
-                       pgstat_info->t_counts.t_tuples_hot_updated++;
+                       pgstattab_info->t_counts.t_tuples_hot_updated++;
        }
 }
 
@@ -382,12 +440,12 @@ pgstat_count_heap_update(Relation rel, bool hot)
 void
 pgstat_count_heap_delete(Relation rel)
 {
-       if (pgstat_should_count_relation(rel))
+       if (pgstat_should_count_table(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-               ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_deleted++;
+               ensure_tabstat_xact_level(pgstattab_info);
+               pgstattab_info->trans->tuples_deleted++;
        }
 }
 
@@ -397,15 +455,15 @@ pgstat_count_heap_delete(Relation rel)
 void
 pgstat_count_truncate(Relation rel)
 {
-       if (pgstat_should_count_relation(rel))
+       if (pgstat_should_count_table(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-               ensure_tabstat_xact_level(pgstat_info);
-               save_truncdrop_counters(pgstat_info->trans, false);
-               pgstat_info->trans->tuples_inserted = 0;
-               pgstat_info->trans->tuples_updated = 0;
-               pgstat_info->trans->tuples_deleted = 0;
+               ensure_tabstat_xact_level(pgstattab_info);
+               save_truncdrop_counters(pgstattab_info->trans, false);
+               pgstattab_info->trans->tuples_inserted = 0;
+               pgstattab_info->trans->tuples_updated = 0;
+               pgstattab_info->trans->tuples_deleted = 0;
        }
 }
 
@@ -420,11 +478,11 @@ pgstat_count_truncate(Relation rel)
 void
 pgstat_update_heap_dead_tuples(Relation rel, int delta)
 {
-       if (pgstat_should_count_relation(rel))
+       if (pgstat_should_count_table(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-               pgstat_info->t_counts.t_delta_dead_tuples -= delta;
+               pgstattab_info->t_counts.t_delta_dead_tuples -= delta;
        }
 }
 
@@ -460,7 +518,42 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid)
        Oid                     dboid = (shared ? InvalidOid : MyDatabaseId);
 
        return (PgStat_StatTabEntry *)
-               pgstat_fetch_entry(PGSTAT_KIND_RELATION, dboid, reloid);
+               pgstat_fetch_entry(PGSTAT_KIND_TABLE, dboid, reloid);
+}
+
+/*
+ * Support function for the SQL-callable pgstat* functions. Returns
+ * the collected statistics for one index or NULL. NULL doesn't mean
+ * that the index doesn't exist, just that there are no statistics, so the
+ * caller is better off to report ZERO instead.
+ */
+PgStat_StatIndEntry *
+pgstat_fetch_stat_indentry(Oid relid)
+{
+       PgStat_StatIndEntry *indentry;
+
+       indentry = pgstat_fetch_stat_indentry_ext(false, relid);
+       if (indentry != NULL)
+               return indentry;
+
+       /*
+        * If we didn't find it, maybe it's a shared index.
+        */
+       indentry = pgstat_fetch_stat_indentry_ext(true, relid);
+       return indentry;
+}
+
+/*
+ * More efficient version of pgstat_fetch_stat_indentry(), allowing to specify
+ * whether the to-be-accessed index is shared or not.
+ */
+PgStat_StatIndEntry *
+pgstat_fetch_stat_indentry_ext(bool shared, Oid reloid)
+{
+       Oid                     dboid = (shared ? InvalidOid : MyDatabaseId);
+
+       return (PgStat_StatIndEntry *)
+               pgstat_fetch_entry(PGSTAT_KIND_INDEX, dboid, reloid);
 }
 
 /*
@@ -476,9 +569,31 @@ find_tabstat_entry(Oid rel_id)
 {
        PgStat_EntryRef *entry_ref;
 
-       entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, 
MyDatabaseId, rel_id);
+       entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_TABLE, MyDatabaseId, 
rel_id);
+       if (!entry_ref)
+               entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_TABLE, 
InvalidOid, rel_id);
+
+       if (entry_ref)
+               return entry_ref->pending;
+       return NULL;
+}
+
+/*
+ * find any existing PgStat_IndexStatus entry for rel
+ *
+ * Find any existing PgStat_IndexStatus entry for rel_id in the current
+ * database. If not found, try finding from shared indexes.
+ *
+ * If no entry found, return NULL, don't create a new one
+ */
+PgStat_IndexStatus *
+find_indstat_entry(Oid rel_id)
+{
+       PgStat_EntryRef *entry_ref;
+
+       entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_INDEX, MyDatabaseId, 
rel_id);
        if (!entry_ref)
-               entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_RELATION, 
InvalidOid, rel_id);
+               entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_INDEX, 
InvalidOid, rel_id);
 
        if (entry_ref)
                return entry_ref->pending;
@@ -694,27 +809,27 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
                                                   void *recdata, uint32 len)
 {
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
-       PgStat_TableStatus *pgstat_info;
+       PgStat_TableStatus *pgstattab_info;
 
        /* Find or create a tabstat entry for the rel */
-       pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
+       pgstattab_info = pgstat_prep_table_pending(rec->t_id, rec->t_shared);
 
        /* Same math as in AtEOXact_PgStat, commit case */
-       pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
-       pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
-       pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
-       pgstat_info->t_counts.t_truncdropped = rec->t_truncdropped;
+       pgstattab_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+       pgstattab_info->t_counts.t_tuples_updated += rec->tuples_updated;
+       pgstattab_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+       pgstattab_info->t_counts.t_truncdropped = rec->t_truncdropped;
        if (rec->t_truncdropped)
        {
                /* forget live/dead stats seen by backend thus far */
-               pgstat_info->t_counts.t_delta_live_tuples = 0;
-               pgstat_info->t_counts.t_delta_dead_tuples = 0;
+               pgstattab_info->t_counts.t_delta_live_tuples = 0;
+               pgstattab_info->t_counts.t_delta_dead_tuples = 0;
        }
-       pgstat_info->t_counts.t_delta_live_tuples +=
+       pgstattab_info->t_counts.t_delta_live_tuples +=
                rec->tuples_inserted - rec->tuples_deleted;
-       pgstat_info->t_counts.t_delta_dead_tuples +=
+       pgstattab_info->t_counts.t_delta_dead_tuples +=
                rec->tuples_updated + rec->tuples_deleted;
-       pgstat_info->t_counts.t_changed_tuples +=
+       pgstattab_info->t_counts.t_changed_tuples +=
                rec->tuples_inserted + rec->tuples_updated +
                rec->tuples_deleted;
 }
@@ -730,10 +845,10 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
                                                  void *recdata, uint32 len)
 {
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
-       PgStat_TableStatus *pgstat_info;
+       PgStat_TableStatus *pgstattab_info;
 
        /* Find or create a tabstat entry for the rel */
-       pgstat_info = pgstat_prep_relation_pending(rec->t_id, rec->t_shared);
+       pgstattab_info = pgstat_prep_table_pending(rec->t_id, rec->t_shared);
 
        /* Same math as in AtEOXact_PgStat, abort case */
        if (rec->t_truncdropped)
@@ -742,10 +857,10 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
                rec->tuples_updated = rec->updated_pre_truncdrop;
                rec->tuples_deleted = rec->deleted_pre_truncdrop;
        }
-       pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
-       pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
-       pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
-       pgstat_info->t_counts.t_delta_dead_tuples +=
+       pgstattab_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
+       pgstattab_info->t_counts.t_tuples_updated += rec->tuples_updated;
+       pgstattab_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
+       pgstattab_info->t_counts.t_delta_dead_tuples +=
                rec->tuples_inserted + rec->tuples_updated;
 }
 
@@ -759,22 +874,21 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
  * entry when successfully flushing.
  */
 bool
-pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
+pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 {
        static const PgStat_TableCounts all_zeroes;
        Oid                     dboid;
        PgStat_TableStatus *lstats; /* pending stats entry  */
-       PgStatShared_Relation *shtabstats;
+       PgStatShared_Table *shtabstats;
        PgStat_StatTabEntry *tabentry;  /* table entry of shared stats */
        PgStat_StatDBEntry *dbentry;    /* pending database entry */
 
        dboid = entry_ref->shared_entry->key.dboid;
        lstats = (PgStat_TableStatus *) entry_ref->pending;
-       shtabstats = (PgStatShared_Relation *) entry_ref->shared_stats;
+       shtabstats = (PgStatShared_Table *) entry_ref->shared_stats;
 
        /*
-        * Ignore entries that didn't accumulate any actual counts, such as
-        * indexes that were opened by the planner but not used.
+        * Ignore entries that didn't accumulate any actual counts.
         */
        if (memcmp(&lstats->t_counts, &all_zeroes,
                           sizeof(PgStat_TableCounts)) == 0)
@@ -792,6 +906,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        if (lstats->t_counts.t_numscans)
        {
                TimestampTz t = GetCurrentTransactionStopTimestamp();
+
                if (t > tabentry->lastscan)
                        tabentry->lastscan = t;
        }
@@ -839,8 +954,74 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        return true;
 }
 
+/*
+ * Flush out pending stats for the entry
+ *
+ * If nowait is true, this function returns false if lock could not
+ * immediately acquired, otherwise true is returned.
+ *
+ * Some of the stats are copied to the corresponding pending database stats
+ * entry when successfully flushing.
+ */
+bool
+pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
+{
+       static const PgStat_IndexCounts all_zeroes;
+       Oid                     dboid;
+
+       PgStat_IndexStatus *lstats; /* pending stats entry  */
+       PgStatShared_Index *shrelcomstats;
+       PgStat_StatIndEntry *indentry;  /* index entry of shared stats */
+       PgStat_StatDBEntry *dbentry;    /* pending database entry */
+
+       dboid = entry_ref->shared_entry->key.dboid;
+       lstats = (PgStat_IndexStatus *) entry_ref->pending;
+       shrelcomstats = (PgStatShared_Index *) entry_ref->shared_stats;
+
+       /*
+        * Ignore entries that didn't accumulate any actual counts, such as
+        * indexes that were opened by the planner but not used.
+        */
+       if (memcmp(&lstats->i_counts, &all_zeroes,
+                          sizeof(PgStat_IndexCounts)) == 0)
+       {
+               return true;
+       }
+
+       if (!pgstat_lock_entry(entry_ref, nowait))
+               return false;
+
+       /* add the values to the shared entry. */
+       indentry = &shrelcomstats->stats;
+
+       indentry->numscans += lstats->i_counts.i_numscans;
+
+       if (lstats->i_counts.i_numscans)
+       {
+               TimestampTz t = GetCurrentTransactionStopTimestamp();
+
+               if (t > indentry->lastscan)
+                       indentry->lastscan = t;
+       }
+       indentry->tuples_returned += lstats->i_counts.i_tuples_returned;
+       indentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+       indentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+       indentry->blocks_hit += lstats->i_counts.i_blocks_hit;
+
+       pgstat_unlock_entry(entry_ref);
+
+       /* The entry was successfully flushed, add the same to database stats */
+       dbentry = pgstat_prep_database_pending(dboid);
+       dbentry->n_tuples_returned += lstats->i_counts.i_tuples_returned;
+       dbentry->n_tuples_fetched += lstats->i_counts.i_tuples_fetched;
+       dbentry->n_blocks_fetched += lstats->i_counts.i_blocks_fetched;
+       dbentry->n_blocks_hit += lstats->i_counts.i_blocks_hit;
+
+       return true;
+}
+
 void
-pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
+pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref)
 {
        PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending;
 
@@ -848,17 +1029,26 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef 
*entry_ref)
                pgstat_unlink_relation(pending->relation);
 }
 
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+       PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+       if (pending->relation)
+               pgstat_unlink_relation(pending->relation);
+}
+
 /*
  * Find or create a PgStat_TableStatus entry for rel. New entry is created and
  * initialized if not exists.
  */
 static PgStat_TableStatus *
-pgstat_prep_relation_pending(Oid rel_id, bool isshared)
+pgstat_prep_table_pending(Oid rel_id, bool isshared)
 {
        PgStat_EntryRef *entry_ref;
        PgStat_TableStatus *pending;
 
-       entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_RELATION,
+       entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_TABLE,
                                                                                
  isshared ? InvalidOid : MyDatabaseId,
                                                                                
  rel_id, NULL);
        pending = entry_ref->pending;
@@ -868,11 +1058,31 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
        return pending;
 }
 
+/*
+ * Find or create a PgStat_IndexStatus entry for rel. New entry is created and
+ * initialized if not exists.
+ */
+static PgStat_IndexStatus *
+pgstat_prep_index_pending(Oid rel_id, bool isshared)
+{
+       PgStat_EntryRef *entry_ref;
+       PgStat_IndexStatus *pending;
+
+       entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INDEX,
+                                                                               
  isshared ? InvalidOid : MyDatabaseId,
+                                                                               
  rel_id, NULL);
+       pending = entry_ref->pending;
+       pending->r_id = rel_id;
+       pending->r_shared = isshared;
+
+       return pending;
+}
+
 /*
  * add a new (sub)transaction state record
  */
 static void
-add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level)
+add_tabstat_xact_level(PgStat_TableStatus *pgstattab_info, int nest_level)
 {
        PgStat_SubXactStatus *xact_state;
        PgStat_TableXactStatus *trans;
@@ -888,24 +1098,24 @@ add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, 
int nest_level)
                MemoryContextAllocZero(TopTransactionContext,
                                                           
sizeof(PgStat_TableXactStatus));
        trans->nest_level = nest_level;
-       trans->upper = pgstat_info->trans;
-       trans->parent = pgstat_info;
+       trans->upper = pgstattab_info->trans;
+       trans->parent = pgstattab_info;
        trans->next = xact_state->first;
        xact_state->first = trans;
-       pgstat_info->trans = trans;
+       pgstattab_info->trans = trans;
 }
 
 /*
  * Add a new (sub)transaction record if needed.
  */
 static void
-ensure_tabstat_xact_level(PgStat_TableStatus *pgstat_info)
+ensure_tabstat_xact_level(PgStat_TableStatus *pgstattab_info)
 {
        int                     nest_level = GetCurrentTransactionNestLevel();
 
-       if (pgstat_info->trans == NULL ||
-               pgstat_info->trans->nest_level != nest_level)
-               add_tabstat_xact_level(pgstat_info, nest_level);
+       if (pgstattab_info->trans == NULL ||
+               pgstattab_info->trans->nest_level != nest_level)
+               add_tabstat_xact_level(pgstattab_info, nest_level);
 }
 
 /*
diff --git a/src/backend/utils/adt/pgstatfuncs.c 
b/src/backend/utils/adt/pgstatfuncs.c
index 96bffc0f2a..e1a5347e13 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -37,23 +37,37 @@
 #define HAS_PGSTAT_PERMISSIONS(role)    (has_privs_of_role(GetUserId(), 
ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
 Datum
-pg_stat_get_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_index_numscans(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatIndEntry *indentry;
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
                result = 0;
        else
-               result = (int64) (tabentry->numscans);
+               result = (int64) (indentry->numscans);
 
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_heap_numscans(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatTabEntry *relentry;
+
+       if ((relentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (relentry->numscans);
+
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_lastscan(PG_FUNCTION_ARGS)
+pg_stat_get_heap_lastscan(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        PgStat_StatTabEntry *tabentry;
@@ -61,12 +75,23 @@ pg_stat_get_lastscan(PG_FUNCTION_ARGS)
        if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
                PG_RETURN_NULL();
        else
-               PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);
+               PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);;
 }
 
+Datum
+pg_stat_get_index_lastscan(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       PgStat_StatIndEntry *indentry;
+
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+               PG_RETURN_NULL();
+       else
+               PG_RETURN_TIMESTAMPTZ(indentry->lastscan);;
+}
 
 Datum
-pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
+pg_stat_get_heap_tuples_returned(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -79,10 +104,23 @@ pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
 
        PG_RETURN_INT64(result);
 }
+Datum
+pg_stat_get_index_tuples_returned(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatIndEntry *indentry;
+
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (indentry->tuples_returned);
 
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_heap_tuples_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -96,6 +134,21 @@ pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_index_tuples_fetched(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatIndEntry *indentry;
+
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (indentry->tuples_fetched);
+
+       PG_RETURN_INT64(result);
+}
+
 
 Datum
 pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS)
@@ -226,7 +279,7 @@ pg_stat_get_ins_since_vacuum(PG_FUNCTION_ARGS)
 
 
 Datum
-pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_heap_blocks_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -240,9 +293,23 @@ pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_index_blocks_fetched(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatIndEntry *indentry;
+
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (indentry->blocks_fetched);
+
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
+pg_stat_get_heap_blocks_hit(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -256,6 +323,21 @@ pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_index_blocks_hit(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatIndEntry *indentry;
+
+       if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (indentry->blocks_hit);
+
+       PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_last_vacuum_time(PG_FUNCTION_ARGS)
 {
@@ -1837,7 +1919,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_heap_xact_numscans(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -1851,17 +1933,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_index_xact_numscans(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_IndexStatus *indentry;
+
+       if ((indentry = find_indstat_entry(relid)) == NULL)
+               result = 0;
+       else
+               result = (int64) (indentry->i_counts.i_numscans);
+
+       PG_RETURN_INT64(result);
+}
+
 Datum
 pg_stat_get_xact_tuples_returned(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_TableStatus *tabentry;
+       PgStat_IndexStatus *indentry;
 
-       if ((tabentry = find_tabstat_entry(relid)) == NULL)
+       if ((indentry = find_indstat_entry(relid)) == NULL)
                result = 0;
        else
-               result = (int64) (tabentry->t_counts.t_tuples_returned);
+               result = (int64) (indentry->i_counts.i_tuples_returned);
 
        PG_RETURN_INT64(result);
 }
@@ -1871,12 +1968,12 @@ pg_stat_get_xact_tuples_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_TableStatus *tabentry;
+       PgStat_IndexStatus *indentry;
 
-       if ((tabentry = find_tabstat_entry(relid)) == NULL)
+       if ((indentry = find_indstat_entry(relid)) == NULL)
                result = 0;
        else
-               result = (int64) (tabentry->t_counts.t_tuples_fetched);
+               result = (int64) (indentry->i_counts.i_tuples_fetched);
 
        PG_RETURN_INT64(result);
 }
@@ -1964,12 +2061,12 @@ pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_TableStatus *tabentry;
+       PgStat_IndexStatus *indentry;
 
-       if ((tabentry = find_tabstat_entry(relid)) == NULL)
+       if ((indentry = find_indstat_entry(relid)) == NULL)
                result = 0;
        else
-               result = (int64) (tabentry->t_counts.t_blocks_fetched);
+               result = (int64) (indentry->i_counts.i_blocks_fetched);
 
        PG_RETURN_INT64(result);
 }
@@ -1979,12 +2076,12 @@ pg_stat_get_xact_blocks_hit(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_TableStatus *tabentry;
+       PgStat_IndexStatus *indentry;
 
-       if ((tabentry = find_tabstat_entry(relid)) == NULL)
+       if ((indentry = find_indstat_entry(relid)) == NULL)
                result = 0;
        else
-               result = (int64) (tabentry->t_counts.t_blocks_hit);
+               result = (int64) (indentry->i_counts.i_blocks_hit);
 
        PG_RETURN_INT64(result);
 }
@@ -2103,7 +2200,8 @@ pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS)
 {
        Oid                     taboid = PG_GETARG_OID(0);
 
-       pgstat_reset(PGSTAT_KIND_RELATION, MyDatabaseId, taboid);
+       pgstat_reset(PGSTAT_KIND_TABLE, MyDatabaseId, taboid);
+       pgstat_reset(PGSTAT_KIND_INDEX, MyDatabaseId, taboid);
 
        PG_RETURN_VOID();
 }
diff --git a/src/backend/utils/cache/relcache.c 
b/src/backend/utils/cache/relcache.c
index bd6cd4e47b..2d24abc3df 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2720,8 +2720,9 @@ RelationClearRelation(Relation relation, bool rebuild)
                        SWAPFIELD(RowSecurityDesc *, rd_rsdesc);
                /* toast OID override must be preserved */
                SWAPFIELD(Oid, rd_toastoid);
-               /* pgstat_info / enabled must be preserved */
-               SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
+               /* pgstattab_info / pgstatind_info / enabled must be preserved 
*/
+               SWAPFIELD(struct PgStat_TableStatus *, pgstattab_info);
+               SWAPFIELD(struct PgStat_IndexStatus *, pgstatind_info);
                SWAPFIELD(bool, pgstat_enabled);
                /* preserve old partition key if we have one */
                if (keep_partkey)
@@ -6314,7 +6315,8 @@ load_relcache_init_file(bool shared)
                rel->rd_firstRelfilelocatorSubid = InvalidSubTransactionId;
                rel->rd_droppedSubid = InvalidSubTransactionId;
                rel->rd_amcache = NULL;
-               rel->pgstat_info = NULL;
+               rel->pgstattab_info = NULL;
+               rel->pgstatind_info = NULL;
 
                /*
                 * Recompute lock and physical addressing info.  This is needed 
in
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 20f5aa56ea..7733ebfcb9 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5248,22 +5248,38 @@
   proargnames => '{mcv_list,index,values,nulls,frequency,base_frequency}',
   prosrc => 'pg_stats_ext_mcvlist_items' },
 
-{ oid => '1928', descr => 'statistics: number of scans done for table/index',
-  proname => 'pg_stat_get_numscans', provolatile => 's', proparallel => 'r',
+{ oid => '1928', descr => 'statistics: number of scans done for table',
+  proname => 'pg_stat_get_heap_numscans', provolatile => 's', proparallel => 
'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_numscans' },
-{ oid => '9976', descr => 'statistics: time of the last scan for table/index',
-  proname => 'pg_stat_get_lastscan', provolatile => 's', proparallel => 'r',
+  prosrc => 'pg_stat_get_heap_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_index_numscans', provolatile => 's', proparallel => 
'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_index_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_heap_lastscan', provolatile => 's', proparallel => 
'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_heap_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_index_lastscan', provolatile => 's', proparallel => 
'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_index_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_heap_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_heap_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_index_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_index_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_heap_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_heap_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_index_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_index_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
   proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
@@ -5299,13 +5315,21 @@
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
   prosrc => 'pg_stat_get_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_heap_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_heap_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_index_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_index_blocks_fetched' },
 { oid => '1935', descr => 'statistics: number of blocks found in cache',
-  proname => 'pg_stat_get_blocks_hit', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_heap_blocks_hit', provolatile => 's', proparallel => 
'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_heap_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_index_blocks_hit', provolatile => 's', proparallel 
=> 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_index_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',
@@ -5693,10 +5717,15 @@
   prosrc => 'pg_stat_get_function_self_time' },
 
 { oid => '3037',
-  descr => 'statistics: number of scans done for table/index in current 
transaction',
-  proname => 'pg_stat_get_xact_numscans', provolatile => 'v',
+  descr => 'statistics: number of scans done for table in current transaction',
+  proname => 'pg_stat_get_heap_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_heap_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_index_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_index_xact_numscans' },
 { oid => '3038',
   descr => 'statistics: number of tuples read by seqscan in current 
transaction',
   proname => 'pg_stat_get_xact_tuples_returned', provolatile => 'v',
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 9e2ce6f011..65b1afe971 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -39,7 +39,8 @@ typedef enum PgStat_Kind
 
        /* stats for variable-numbered objects */
        PGSTAT_KIND_DATABASE,           /* database-wide statistics */
-       PGSTAT_KIND_RELATION,           /* per-table statistics */
+       PGSTAT_KIND_TABLE,                      /* per-table statistics */
+       PGSTAT_KIND_INDEX,                      /* per-index statistics */
        PGSTAT_KIND_FUNCTION,           /* per-function statistics */
        PGSTAT_KIND_REPLSLOT,           /* per-slot statistics */
        PGSTAT_KIND_SUBSCRIPTION,       /* per-subscription statistics */
@@ -145,6 +146,28 @@ typedef struct PgStat_BackendSubEntry
        PgStat_Counter sync_error_count;
 } PgStat_BackendSubEntry;
 
+/* ----------
+ * PgStat_IndexCounts                  The actual per-index counts kept by a 
backend
+ *
+ * This struct should contain only actual event counters, because we memcmp
+ * it against zeroes to detect whether there are any stats updates to apply.
+ * It is a component of PgStat_IndexStatus (within-backend state).
+ *
+ * tuples_returned is the number of index entries returned by
+ * the index AM, while tuples_fetched is the number of tuples successfully
+ * fetched by heap_fetch under the control of simple indexscans for this index.
+ * ----------
+ */
+typedef struct PgStat_IndexCounts
+{
+       PgStat_Counter i_numscans;
+
+       PgStat_Counter i_tuples_returned;
+       PgStat_Counter i_tuples_fetched;
+       PgStat_Counter i_blocks_fetched;
+       PgStat_Counter i_blocks_hit;
+} PgStat_IndexCounts;
+
 /* ----------
  * PgStat_TableCounts                  The actual per-table counts kept by a 
backend
  *
@@ -152,12 +175,9 @@ typedef struct PgStat_BackendSubEntry
  * it against zeroes to detect whether there are any stats updates to apply.
  * It is a component of PgStat_TableStatus (within-backend state).
  *
- * Note: for a table, tuples_returned is the number of tuples successfully
+ * Note: tuples_returned is the number of tuples successfully
  * fetched by heap_getnext, while tuples_fetched is the number of tuples
  * successfully fetched by heap_fetch under the control of bitmap indexscans.
- * For an index, tuples_returned is the number of index entries returned by
- * the index AM, while tuples_fetched is the number of tuples successfully
- * fetched by heap_fetch under the control of simple indexscans for this index.
  *
  * tuples_inserted/updated/deleted/hot_updated count attempted actions,
  * regardless of whether the transaction committed.  delta_live_tuples,
@@ -210,6 +230,22 @@ typedef struct PgStat_TableStatus
        Relation        relation;               /* rel that is using this entry 
*/
 } PgStat_TableStatus;
 
+/* ----------
+ * PgStat_IndexStatus                  Per-index status within a backend
+ *
+ * Many of the event counters are nontransactional, ie, we count events
+ * in committed and aborted transactions alike.  For these, we just count
+ * directly in the PgStat_IndexStatus.
+ * ----------
+ */
+typedef struct PgStat_IndexStatus
+{
+       Oid                     r_id;                   /* relation's OID */
+       bool            r_shared;               /* is it a shared catalog? */
+       PgStat_IndexCounts i_counts;    /* event counts to be sent */
+       Relation        relation;               /* rel that is using this entry 
*/
+} PgStat_IndexStatus;
+
 /* ----------
  * PgStat_TableXactStatus              Per-table, per-subtransaction status
  * ----------
@@ -382,6 +418,17 @@ typedef struct PgStat_StatTabEntry
        PgStat_Counter autovac_analyze_count;
 } PgStat_StatTabEntry;
 
+typedef struct PgStat_StatIndEntry
+{
+       PgStat_Counter numscans;
+       TimestampTz lastscan;
+
+       PgStat_Counter tuples_returned;
+       PgStat_Counter tuples_fetched;
+       PgStat_Counter blocks_fetched;
+       PgStat_Counter blocks_hit;
+} PgStat_StatIndEntry;
+
 typedef struct PgStat_WalStats
 {
        PgStat_Counter wal_records;
@@ -498,11 +545,15 @@ extern PgStat_BackendFunctionEntry 
*find_funcstat_entry(Oid func_id);
  */
 
 extern void pgstat_create_relation(Relation rel);
-extern void pgstat_drop_relation(Relation rel);
-extern void pgstat_copy_relation_stats(Relation dst, Relation src);
+extern void pgstat_create_table(Relation rel);
+extern void pgstat_drop_heap(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
 
 extern void pgstat_init_relation(Relation rel);
 extern void pgstat_assoc_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
 extern void pgstat_unlink_relation(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
@@ -513,51 +564,69 @@ extern void pgstat_report_analyze(Relation rel,
 
 /*
  * If stats are enabled, but pending data hasn't been prepared yet, call
- * pgstat_assoc_relation() to do so. See its comment for why this is done
- * separately from pgstat_init_relation().
+ * pgstat_assoc_table() / pgstat_assoc_index() to do so.
+ * See their comment for why this is done separately from 
pgstat_init_relation().
  */
-#define pgstat_should_count_relation(rel)                           \
-       (likely((rel)->pgstat_info != NULL) ? true :                    \
-        ((rel)->pgstat_enabled ? pgstat_assoc_relation(rel), true : false))
+#define pgstat_should_count_table(rel)                           \
+       (likely((rel)->pgstattab_info != NULL) ? true :                    \
+        ((rel)->pgstat_enabled ? pgstat_assoc_table(rel), true : false))
+
+#define pgstat_should_count_index(rel)                           \
+       (likely((rel)->pgstatind_info != NULL) ? true :                    \
+        ((rel)->pgstat_enabled ? pgstat_assoc_index(rel), true : false))
 
 /* nontransactional event counts are simple enough to inline */
 
 #define pgstat_count_heap_scan(rel)                                            
                        \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_numscans++;              
                \
+               if (pgstat_should_count_table(rel))                             
                \
+                       (rel)->pgstattab_info->t_counts.t_numscans++;           
                \
        } while (0)
 #define pgstat_count_heap_getnext(rel)                                         
                \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_tuples_returned++;       
        \
+               if (pgstat_should_count_table(rel))                             
                \
+                       (rel)->pgstattab_info->t_counts.t_tuples_returned++;    
        \
        } while (0)
 #define pgstat_count_heap_fetch(rel)                                           
                \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_tuples_fetched++;        
        \
+               if (pgstat_should_count_table(rel))                             
                \
+                       (rel)->pgstattab_info->t_counts.t_tuples_fetched++;     
        \
+       } while (0)
+#define pgstat_count_index_fetch(rel)                                          
                \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_index(rel))                             
                \
+                       (rel)->pgstatind_info->i_counts.i_tuples_fetched++;     
        \
        } while (0)
 #define pgstat_count_index_scan(rel)                                           
                \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_numscans++;              
                \
+               if (pgstat_should_count_index(rel))                             
                \
+                       (rel)->pgstatind_info->i_counts.i_numscans++;           
        \
        } while (0)
 #define pgstat_count_index_tuples(rel, n)                                      
                \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_tuples_returned += (n);  
\
+               if (pgstat_should_count_index(rel))                             
                \
+                       (rel)->pgstatind_info->i_counts.i_tuples_returned += 
(n);       \
        } while (0)
-#define pgstat_count_buffer_read(rel)                                          
                \
+#define pgstat_count_heap_buffer_read(rel)                                     
                        \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_blocks_fetched++;        
        \
+               if (pgstat_should_count_table(rel))     \
+                       (rel)->pgstattab_info->t_counts.t_blocks_fetched++;     
        \
        } while (0)
-#define pgstat_count_buffer_hit(rel)                                           
                \
+#define pgstat_count_index_buffer_read(rel)                                    
                        \
        do {                                                                    
                                                \
-               if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->t_counts.t_blocks_hit++;            
        \
+               if (pgstat_should_count_index(rel))     \
+                       (rel)->pgstatind_info->i_counts.i_blocks_fetched++;     
        \
+       } while (0)
+#define pgstat_count_heap_buffer_hit(rel)                                      
                        \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_table(rel))     \
+                       (rel)->pgstattab_info->t_counts.t_blocks_hit++; \
+       } while (0)
+#define pgstat_count_index_buffer_hit(rel)                                     
                        \
+       do {                                                                    
                                                \
+               if (pgstat_should_count_index(rel))     \
+                       (rel)->pgstatind_info->i_counts.i_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);
 extern void pgstat_count_heap_delete(Relation rel);
@@ -573,6 +642,10 @@ extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid 
relid);
 extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared,
                                                                                
                                   Oid reloid);
 extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
+extern PgStat_IndexStatus *find_indstat_entry(Oid rel_id);
+extern PgStat_StatIndEntry *pgstat_fetch_stat_indentry(Oid relid);
+extern PgStat_StatIndEntry *pgstat_fetch_stat_indentry_ext(bool shared,
+                                                                               
                                   Oid relid);
 
 
 /*
diff --git a/src/include/utils/pgstat_internal.h 
b/src/include/utils/pgstat_internal.h
index c869533b28..b7464b5a5c 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -360,11 +360,17 @@ typedef struct PgStatShared_Database
        PgStat_StatDBEntry stats;
 } PgStatShared_Database;
 
-typedef struct PgStatShared_Relation
+typedef struct PgStatShared_Table
 {
        PgStatShared_Common header;
        PgStat_StatTabEntry stats;
-} PgStatShared_Relation;
+} PgStatShared_Table;
+
+typedef struct PgStatShared_Index
+{
+       PgStatShared_Common header;
+       PgStat_StatIndEntry stats;
+} PgStatShared_Index;
 
 typedef struct PgStatShared_Function
 {
@@ -559,8 +565,10 @@ extern void 
AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool
 extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
 extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
 
-extern bool pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
-extern void pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref);
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern bool pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+extern void pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref);
 
 
 /*
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index f383a2fca9..796423f3e3 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -248,7 +248,10 @@ typedef struct RelationData
 
        bool            pgstat_enabled; /* should relation stats be counted */
        /* use "struct" here to avoid needing to include pgstat.h: */
-       struct PgStat_TableStatus *pgstat_info; /* statistics collection area */
+       /* table's statistics collection area */
+       struct PgStat_TableStatus *pgstattab_info;
+       /* Index's statistics collection area */
+       struct PgStat_IndexStatus *pgstatind_info;
 } RelationData;
 
 
diff --git a/src/test/isolation/expected/stats.out 
b/src/test/isolation/expected/stats.out
index 61b5a710ec..6ff2cfbd97 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,8 +2166,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2210,8 +2210,8 @@ step s2_table_update_k1: UPDATE test_stat_tab SET value = 
value + 1 WHERE key =
 step s1_table_drop: DROP TABLE test_stat_tab;
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2236,8 +2236,8 @@ pg_stat_force_next_flush
 step s1_track_counts_off: SET track_counts = off;
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2275,8 +2275,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2314,8 +2314,8 @@ pg_stat_force_next_flush
 step s1_track_counts_off: SET track_counts = off;
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2369,8 +2369,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2401,8 +2401,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2463,8 +2463,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2495,8 +2495,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2558,8 +2558,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2627,8 +2627,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2688,8 +2688,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2755,8 +2755,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2795,8 +2795,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2841,8 +2841,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2881,8 +2881,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2927,8 +2927,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -2968,8 +2968,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
@@ -3015,8 +3015,8 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
diff --git a/src/test/isolation/specs/stats.spec 
b/src/test/isolation/specs/stats.spec
index 5b922d788c..0ca25393e4 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,8 +93,8 @@ step s1_table_drop { DROP TABLE test_stat_tab; }
 
 step s1_table_stats {
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
-        pg_stat_get_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_heap_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_heap_tuples_returned(tso.oid) AS seq_tup_read,
         pg_stat_get_tuples_inserted(tso.oid) AS n_tup_ins,
         pg_stat_get_tuples_updated(tso.oid) AS n_tup_upd,
         pg_stat_get_tuples_deleted(tso.oid) AS n_tup_del,
diff --git a/src/test/recovery/t/029_stats_restart.pl 
b/src/test/recovery/t/029_stats_restart.pl
index 1bf7b568cc..c25634d33f 100644
--- a/src/test/recovery/t/029_stats_restart.pl
+++ b/src/test/recovery/t/029_stats_restart.pl
@@ -43,8 +43,8 @@ my $sect = "initial";
 is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
 is(have_stats('function', $dboid, $funcoid),
        't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
-       't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+       't', "$sect: table stats do exist");
 
 # regular shutdown
 $node->stop();
@@ -67,8 +67,8 @@ $sect = "copy";
 is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
 is(have_stats('function', $dboid, $funcoid),
        't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
-       't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+       't', "$sect: table stats do exist");
 
 $node->stop('immediate');
 
@@ -84,8 +84,8 @@ $sect = "post immediate";
 is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
 is(have_stats('function', $dboid, $funcoid),
        'f', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
-       'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid),
+       'f', "$sect: table stats do not exist");
 
 # get rid of backup statsfile
 unlink $statsfile or die "cannot unlink $statsfile $!";
@@ -98,8 +98,8 @@ $sect = "post immediate, new";
 is(have_stats('database', $dboid, 0), 't', "$sect: db stats do exist");
 is(have_stats('function', $dboid, $funcoid),
        't', "$sect: function stats do exist");
-is(have_stats('relation', $dboid, $tableoid),
-       't', "$sect: relation stats do exist");
+is(have_stats('table', $dboid, $tableoid),
+       't', "$sect: table stats do exist");
 
 # regular shutdown
 $node->stop();
@@ -117,8 +117,7 @@ $sect = "invalid_overwrite";
 is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
 is(have_stats('function', $dboid, $funcoid),
        'f', "$sect: function stats do not exist");
-is(have_stats('relation', $dboid, $tableoid),
-       'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid), 'f', "$sect: table do not exist");
 
 
 ## check invalid stats file starting with valid contents, but followed by
@@ -133,8 +132,8 @@ $sect = "invalid_append";
 is(have_stats('database', $dboid, 0), 'f', "$sect: db stats do not exist");
 is(have_stats('function', $dboid, $funcoid),
        'f', "$sect: function stats do not exist");
-is(have_stats('relation', $dboid, $tableoid),
-       'f', "$sect: relation stats do not exist");
+is(have_stats('table', $dboid, $tableoid),
+       'f', "$sect: table stats do not exist");
 
 
 ## checks related to stats persistency around restarts and resets
diff --git a/src/test/recovery/t/030_stats_cleanup_replica.pl 
b/src/test/recovery/t/030_stats_cleanup_replica.pl
index cc92ddbb52..1e78b13cf9 100644
--- a/src/test/recovery/t/030_stats_cleanup_replica.pl
+++ b/src/test/recovery/t/030_stats_cleanup_replica.pl
@@ -185,7 +185,7 @@ sub test_standby_func_tab_stats_status
        my %stats;
 
        $stats{rel} = $node_standby->safe_psql($connect_db,
-               "SELECT pg_stat_have_stats('relation', $dboid, $tableoid)");
+               "SELECT pg_stat_have_stats('table', $dboid, $tableoid)");
        $stats{func} = $node_standby->safe_psql($connect_db,
                "SELECT pg_stat_have_stats('function', $dboid, $funcoid)");
 
diff --git a/src/test/regress/expected/rules.out 
b/src/test/regress/expected/rules.out
index 624d0e5aae..531164e030 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1764,10 +1764,10 @@ pg_stat_all_indexes| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
     i.relname AS indexrelname,
-    pg_stat_get_numscans(i.oid) AS idx_scan,
-    pg_stat_get_lastscan(i.oid) AS last_idx_scan,
-    pg_stat_get_tuples_returned(i.oid) AS idx_tup_read,
-    pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch
+    pg_stat_get_index_numscans(i.oid) AS idx_scan,
+    pg_stat_get_index_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_index_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_index_tuples_fetched(i.oid) AS idx_tup_fetch
    FROM (((pg_class c
      JOIN pg_index x ON ((c.oid = x.indrelid)))
      JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -1776,12 +1776,12 @@ pg_stat_all_indexes| SELECT c.oid AS relid,
 pg_stat_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
-    pg_stat_get_numscans(c.oid) AS seq_scan,
-    pg_stat_get_lastscan(c.oid) AS last_seq_scan,
-    pg_stat_get_tuples_returned(c.oid) AS seq_tup_read,
-    (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan,
-    max(pg_stat_get_lastscan(i.indexrelid)) AS last_idx_scan,
-    ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + 
pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_heap_numscans(c.oid) AS seq_scan,
+    pg_stat_get_heap_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_heap_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_index_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_index_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_index_tuples_fetched(i.indexrelid)))::bigint + 
pg_stat_get_heap_tuples_fetched(c.oid)) AS idx_tup_fetch,
     pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins,
     pg_stat_get_tuples_updated(c.oid) AS n_tup_upd,
     pg_stat_get_tuples_deleted(c.oid) AS n_tup_del,
@@ -2221,9 +2221,9 @@ pg_stat_wal_receiver| SELECT s.pid,
 pg_stat_xact_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
-    pg_stat_get_xact_numscans(c.oid) AS seq_scan,
+    pg_stat_get_heap_xact_numscans(c.oid) AS seq_scan,
     pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read,
-    (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan,
+    (sum(pg_stat_get_index_xact_numscans(i.indexrelid)))::bigint AS idx_scan,
     ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + 
pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch,
     pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins,
     pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd,
@@ -2274,8 +2274,8 @@ pg_statio_all_indexes| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     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_index_blocks_fetched(i.oid) - 
pg_stat_get_index_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_index_blocks_hit(i.oid) AS idx_blks_hit
    FROM (((pg_class c
      JOIN pg_index x ON ((c.oid = x.indrelid)))
      JOIN pg_class i ON ((i.oid = x.indexrelid)))
@@ -2284,31 +2284,31 @@ pg_statio_all_indexes| SELECT c.oid AS relid,
 pg_statio_all_sequences| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
-    (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS 
blks_read,
-    pg_stat_get_blocks_hit(c.oid) AS blks_hit
+    (pg_stat_get_index_blocks_fetched(c.oid) - 
pg_stat_get_heap_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_heap_blocks_hit(c.oid) AS blks_hit
    FROM (pg_class c
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
   WHERE (c.relkind = 'S'::"char");
 pg_statio_all_tables| SELECT c.oid AS relid,
     n.nspname AS schemaname,
     c.relname,
-    (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS 
heap_blks_read,
-    pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit,
+    (pg_stat_get_heap_blocks_fetched(c.oid) - 
pg_stat_get_heap_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_heap_blocks_hit(c.oid) AS heap_blks_hit,
     i.idx_blks_read,
     i.idx_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,
+    (pg_stat_get_heap_blocks_fetched(t.oid) - 
pg_stat_get_heap_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_heap_blocks_hit(t.oid) AS toast_blks_hit,
     x.idx_blks_read AS tidx_blks_read,
     x.idx_blks_hit AS tidx_blks_hit
    FROM ((((pg_class c
      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
+     LEFT JOIN LATERAL ( SELECT 
(sum((pg_stat_get_index_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_index_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_index_blocks_hit(pg_index.indexrelid)))::bigint 
AS idx_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,
-            (sum(pg_stat_get_blocks_hit(pg_index.indexrelid)))::bigint AS 
idx_blks_hit
+     LEFT JOIN LATERAL ( SELECT 
(sum((pg_stat_get_index_blocks_fetched(pg_index.indexrelid) - 
pg_stat_get_index_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_index_blocks_hit(pg_index.indexrelid)))::bigint 
AS idx_blks_hit
            FROM pg_index
           WHERE (pg_index.indrelid = t.oid)) x ON (true))
   WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"]));
diff --git a/src/test/regress/expected/stats.out 
b/src/test/regress/expected/stats.out
index 257a6a9da9..449d70f4c7 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -151,6 +151,117 @@ FROM prevstats AS pr;
 
 COMMIT;
 ----
+-- Basic tests for toast
+---
+CREATE TABLE stats_toast(stuff text);
+ALTER TABLE stats_toast ALTER COLUMN stuff SET STORAGE EXTERNAL;
+INSERT INTO stats_toast VALUES (repeat('a',1000000));
+SELECT count(*) FROM stats_toast WHERE stuff like '%a%';
+ count 
+-------
+     1
+(1 row)
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+select toast_blks_read > 0 as toast_blks_read, toast_blks_hit > 0 as 
toast_blks_hit,
+       tidx_blks_read > 0 as tidx_blks_read, tidx_blks_hit > 0 as 
tidx_blks_hit,
+       toast_blks_hit >= toast_blks_read,
+       tidx_blks_hit >= tidx_blks_read
+   from pg_statio_all_tables
+ where relname = 'stats_toast';
+ toast_blks_read | toast_blks_hit | tidx_blks_read | tidx_blks_hit | ?column? 
| ?column? 
+-----------------+----------------+----------------+---------------+----------+----------
+ t               | t              | t              | t             | t        
| t
+(1 row)
+
+----
+-- Basic tests for partition
+---
+CREATE TABLE stats_partition (
+    id1         int not null,
+    id2         int not null
+) PARTITION BY RANGE (id2);
+CREATE TABLE stats_partition_5 PARTITION OF stats_partition
+    FOR VALUES FROM (0) TO (5);
+CREATE TABLE stats_partition_20000 PARTITION OF stats_partition
+    FOR VALUES FROM (5) TO (20001);
+CREATE INDEX ON stats_partition (id2);
+insert into stats_partition select a,a from generate_series(0,20000) a;
+SET enable_seqscan TO off;
+SET enable_bitmapscan TO off;
+select * from stats_partition where id2 = 2000;
+ id1  | id2  
+------+------
+ 2000 | 2000
+(1 row)
+
+select * from stats_partition where id2 = 2;
+ id1 | id2 
+-----+-----
+   2 |   2
+(1 row)
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+ pg_stat_force_next_flush 
+--------------------------
+ 
+(1 row)
+
+select relname, indexrelname, idx_scan > 0 as idx_scan, idx_tup_read > 0 as 
idx_tup_read,
+       idx_tup_fetch > 0 as idx_tup_fetch
+   from pg_stat_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+        relname        |         indexrelname          | idx_scan | 
idx_tup_read | idx_tup_fetch 
+-----------------------+-------------------------------+----------+--------------+---------------
+ stats_partition_20000 | stats_partition_20000_id2_idx | t        | t          
  | t
+ stats_partition_5     | stats_partition_5_id2_idx     | t        | t          
  | t
+(2 rows)
+
+select relname, indexrelname, idx_blks_read > 0 as idx_blks_read,
+       idx_blks_hit > 0 as idx_blks_hit
+   from pg_statio_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+        relname        |         indexrelname          | idx_blks_read | 
idx_blks_hit 
+-----------------------+-------------------------------+---------------+--------------
+ stats_partition_20000 | stats_partition_20000_id2_idx | t             | t
+ stats_partition_5     | stats_partition_5_id2_idx     | t             | t
+(2 rows)
+
+select relname,seq_scan > 0 as seq_scan, seq_tup_read > 0 as seq_tup_read,
+       idx_scan > 0 as idx_scan, idx_tup_fetch > 0 as idx_tup_fetch
+   from pg_stat_all_tables
+  where relname like '%stats_partition%'
+  order by relname COLLATE "C";
+        relname        | seq_scan | seq_tup_read | idx_scan | idx_tup_fetch 
+-----------------------+----------+--------------+----------+---------------
+ stats_partition       | f        | f            | f        | f
+ stats_partition_20000 | t        | f            | t        | t
+ stats_partition_5     | t        | f            | t        | t
+(3 rows)
+
+select relname, heap_blks_read > 0 as heap_blks_read, heap_blks_hit > 0 as 
heap_blks_hit,
+       idx_blks_read > 0 as idx_blks_read, idx_blks_hit > 0 as idx_blks_hit
+   from pg_statio_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+        relname        | heap_blks_read | heap_blks_hit | idx_blks_read | 
idx_blks_hit 
+-----------------------+----------------+---------------+---------------+--------------
+ stats_partition_20000 | t              | t             | t             | t
+ stats_partition_5     | t              | t             | t             | t
+(2 rows)
+
+SET enable_seqscan TO on;
+SET enable_bitmapscan TO on;
+----
 -- Basic tests for track_functions
 ---
 CREATE FUNCTION stats_test_func1() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN 
END;$$;
@@ -1015,21 +1126,21 @@ select a from stats_test_tab1 where a = 3;
  3
 (1 row)
 
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
 (1 row)
 
 -- pg_stat_have_stats returns false for dropped index with stats
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
 (1 row)
 
 DROP index stats_test_idx1;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  f
@@ -1045,14 +1156,14 @@ select a from stats_test_tab1 where a = 3;
  3
 (1 row)
 
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
 (1 row)
 
 ROLLBACK;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  f
@@ -1067,7 +1178,7 @@ select a from stats_test_tab1 where a = 3;
  3
 (1 row)
 
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
@@ -1075,7 +1186,7 @@ SELECT pg_stat_have_stats('relation', :dboid, 
:stats_test_idx1_oid);
 
 REINDEX index CONCURRENTLY stats_test_idx1;
 -- false for previous oid
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  f
@@ -1083,7 +1194,7 @@ SELECT pg_stat_have_stats('relation', :dboid, 
:stats_test_idx1_oid);
 
 -- true for new oid
 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
@@ -1091,7 +1202,7 @@ SELECT pg_stat_have_stats('relation', :dboid, 
:stats_test_idx1_oid);
 
 -- pg_stat_have_stats returns true for a rolled back drop index with stats
 BEGIN;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
@@ -1099,7 +1210,7 @@ SELECT pg_stat_have_stats('relation', :dboid, 
:stats_test_idx1_oid);
 
 DROP index stats_test_idx1;
 ROLLBACK;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
  pg_stat_have_stats 
 --------------------
  t
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index f6270f7bad..c7fee71d6f 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -126,6 +126,77 @@ FROM prevstats AS pr;
 
 COMMIT;
 
+----
+-- Basic tests for toast
+---
+CREATE TABLE stats_toast(stuff text);
+ALTER TABLE stats_toast ALTER COLUMN stuff SET STORAGE EXTERNAL;
+INSERT INTO stats_toast VALUES (repeat('a',1000000));
+SELECT count(*) FROM stats_toast WHERE stuff like '%a%';
+
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+
+select toast_blks_read > 0 as toast_blks_read, toast_blks_hit > 0 as 
toast_blks_hit,
+       tidx_blks_read > 0 as tidx_blks_read, tidx_blks_hit > 0 as 
tidx_blks_hit,
+       toast_blks_hit >= toast_blks_read,
+       tidx_blks_hit >= tidx_blks_read
+   from pg_statio_all_tables
+ where relname = 'stats_toast';
+
+----
+-- Basic tests for partition
+---
+CREATE TABLE stats_partition (
+    id1         int not null,
+    id2         int not null
+) PARTITION BY RANGE (id2);
+
+CREATE TABLE stats_partition_5 PARTITION OF stats_partition
+    FOR VALUES FROM (0) TO (5);
+
+CREATE TABLE stats_partition_20000 PARTITION OF stats_partition
+    FOR VALUES FROM (5) TO (20001);
+
+CREATE INDEX ON stats_partition (id2);
+
+insert into stats_partition select a,a from generate_series(0,20000) a;
+
+SET enable_seqscan TO off;
+SET enable_bitmapscan TO off;
+
+select * from stats_partition where id2 = 2000;
+select * from stats_partition where id2 = 2;
+-- ensure pending stats are flushed
+SELECT pg_stat_force_next_flush();
+
+select relname, indexrelname, idx_scan > 0 as idx_scan, idx_tup_read > 0 as 
idx_tup_read,
+       idx_tup_fetch > 0 as idx_tup_fetch
+   from pg_stat_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+select relname, indexrelname, idx_blks_read > 0 as idx_blks_read,
+       idx_blks_hit > 0 as idx_blks_hit
+   from pg_statio_all_indexes
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+select relname,seq_scan > 0 as seq_scan, seq_tup_read > 0 as seq_tup_read,
+       idx_scan > 0 as idx_scan, idx_tup_fetch > 0 as idx_tup_fetch
+   from pg_stat_all_tables
+  where relname like '%stats_partition%'
+  order by relname COLLATE "C";
+
+select relname, heap_blks_read > 0 as heap_blks_read, heap_blks_hit > 0 as 
heap_blks_hit,
+       idx_blks_read > 0 as idx_blks_read, idx_blks_hit > 0 as idx_blks_hit
+   from pg_statio_all_tables
+ where relname like '%stats_partition%'
+ order by relname COLLATE "C";
+
+SET enable_seqscan TO on;
+SET enable_bitmapscan TO on;
+
 ----
 -- Basic tests for track_functions
 ---
@@ -492,40 +563,40 @@ CREATE index stats_test_idx1 on stats_test_tab1(a);
 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
 SET enable_seqscan TO off;
 select a from stats_test_tab1 where a = 3;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 
 -- pg_stat_have_stats returns false for dropped index with stats
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 DROP index stats_test_idx1;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 
 -- pg_stat_have_stats returns false for rolled back index creation
 BEGIN;
 CREATE index stats_test_idx1 on stats_test_tab1(a);
 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
 select a from stats_test_tab1 where a = 3;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 ROLLBACK;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 
 -- pg_stat_have_stats returns true for reindex CONCURRENTLY
 CREATE index stats_test_idx1 on stats_test_tab1(a);
 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
 select a from stats_test_tab1 where a = 3;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 REINDEX index CONCURRENTLY stats_test_idx1;
 -- false for previous oid
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 -- true for new oid
 SELECT 'stats_test_idx1'::regclass::oid AS stats_test_idx1_oid \gset
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 
 -- pg_stat_have_stats returns true for a rolled back drop index with stats
 BEGIN;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 DROP index stats_test_idx1;
 ROLLBACK;
-SELECT pg_stat_have_stats('relation', :dboid, :stats_test_idx1_oid);
+SELECT pg_stat_have_stats('index', :dboid, :stats_test_idx1_oid);
 
 -- put enable_seqscan back to on
 SET enable_seqscan TO on;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 2f02cc8f42..38806d5dee 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2009,10 +2009,11 @@ PgStatShared_Common
 PgStatShared_Database
 PgStatShared_Function
 PgStatShared_HashEntry
-PgStatShared_Relation
+PgStatShared_Index
 PgStatShared_ReplSlot
 PgStatShared_SLRU
 PgStatShared_Subscription
+PgStatShared_Table
 PgStatShared_Wal
 PgStat_ArchiverStats
 PgStat_BackendFunctionEntry

Reply via email to