Hi,

On 11/1/22 1:30 AM, Andres Freund wrote:
Hi,

On 2022-10-31 14:14:15 +0100, Drouvot, Bertrand wrote:
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].

Thanks for working on this!


Thanks for looking at it!



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);

I don't think "heap" is a good name for these, even if there's some historical
reasons for it. Particularly because you used "table" in some bits and pieces
too.


Agree, replaced by "table" where appropriate in V3 attached.


  /*
@@ -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));
+}

Hm - why is this best handled on this level, rather than at the caller?



Agree that it should be split in pgstat_create_table()/pgstat_create_index() (also as it was already split for the "drop" case): done in V3.

+/*
+ * 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);
  }

Do we need this split anywhere for now? I suspect not, the table case is
mainly for the autovacuum launcher, which won't look at indexes "in isolation".


Yes I think so as pgstat_fetch_stat_indentry_ext() has its use case in pgstat_copy_index_stats() (previously pgstat_copy_relation_stats()).



@@ -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);
+}

We have so many copies of this by now - perhaps we first should deduplicate
them somehow? Even if it's just a macro or such.


Yeah good point, a new macro has been defined for the "int64" return case in V3 attached.

Regards,

--
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
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..5fd42fa189 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -404,7 +404,10 @@ heap_create(const char *relname,
                                                                         
reltablespace);
 
        /* ensure that stats are dropped if transaction aborts */
-       pgstat_create_relation(rel);
+       if (rel->rd_rel->relkind == RELKIND_INDEX)
+               pgstat_create_index(rel);
+       else
+               pgstat_create_table(rel);
 
        return rel;
 }
@@ -1853,7 +1856,7 @@ heap_drop_with_catalog(Oid relid)
                RelationDropStorage(rel);
 
        /* ensure that stats are dropped if transaction commits */
-       pgstat_drop_relation(rel);
+       pgstat_drop_table(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..6f5153bf66 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_table_numscans(C.oid) AS seq_scan,
+            pg_stat_get_table_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_table_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_table_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_table_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_table_blocks_fetched(C.oid) -
+                    pg_stat_get_table_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_table_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_table_blocks_fetched(T.oid) -
+                    pg_stat_get_table_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_table_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_table_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_table_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..f1a7a8aa8d 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_table_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_table_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..13cdd7f849 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,55 +183,88 @@ 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;
+       }
+}
+
+/*
+ * Ensure that table stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_table(Relation rel)
+{
+       pgstat_create_transactional(PGSTAT_KIND_TABLE,
+                                                               
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+                                                               
RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that index stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_index(Relation rel)
 {
-       pgstat_create_transactional(PGSTAT_KIND_RELATION,
+       pgstat_create_transactional(PGSTAT_KIND_INDEX,
                                                                
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
                                                                
RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that index stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_index(Relation rel)
+{
+       pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+                                                         
rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+                                                         
RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that table stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_table(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 +276,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 +288,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 +335,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 +354,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 +412,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 +427,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 +446,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 +461,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 +484,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 +524,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 +575,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_RELATION, 
InvalidOid, rel_id);
+               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_INDEX, 
InvalidOid, rel_id);
 
        if (entry_ref)
                return entry_ref->pending;
@@ -694,27 +815,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 +851,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 +863,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 +880,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 +912,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 +960,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 +1035,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 +1064,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 +1104,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..0dd6148ed1 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,24 +36,34 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)    (has_privs_of_role(GetUserId(), 
ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
+#define PGSTAT_FETCH_STAT_ENTRY(entry, stat_name) ((entry == NULL) ? 0 : 
(int64) (entry->stat_name));
+
 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 = pgstat_fetch_stat_indentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->numscans);
+       result = PGSTAT_FETCH_STAT_ENTRY(indentry, numscans);
 
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_table_numscans(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
+
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, numscans);
+
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_lastscan(PG_FUNCTION_ARGS)
+pg_stat_get_table_lastscan(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        PgStat_StatTabEntry *tabentry;
@@ -64,34 +74,61 @@ pg_stat_get_lastscan(PG_FUNCTION_ARGS)
                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_table_tuples_returned(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_returned);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_returned);
 
        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 = pgstat_fetch_stat_indentry(relid);
 
+       result = PGSTAT_FETCH_STAT_ENTRY(indentry, tuples_returned);
+
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_tuples_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_table_tuples_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_fetched);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_fetched);
+
+       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 = pgstat_fetch_stat_indentry(relid);
+
+       result = PGSTAT_FETCH_STAT_ENTRY(indentry, tuples_fetched);
 
        PG_RETURN_INT64(result);
 }
@@ -102,12 +139,9 @@ pg_stat_get_tuples_inserted(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_inserted);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_inserted);
 
        PG_RETURN_INT64(result);
 }
@@ -118,12 +152,9 @@ pg_stat_get_tuples_updated(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_updated);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_updated);
 
        PG_RETURN_INT64(result);
 }
@@ -134,12 +165,9 @@ pg_stat_get_tuples_deleted(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_deleted);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_deleted);
 
        PG_RETURN_INT64(result);
 }
@@ -150,12 +178,9 @@ pg_stat_get_tuples_hot_updated(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->tuples_hot_updated);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, tuples_hot_updated);
 
        PG_RETURN_INT64(result);
 }
@@ -166,12 +191,9 @@ pg_stat_get_live_tuples(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->n_live_tuples);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, n_live_tuples);
 
        PG_RETURN_INT64(result);
 }
@@ -182,12 +204,9 @@ pg_stat_get_dead_tuples(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->n_dead_tuples);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, n_dead_tuples);
 
        PG_RETURN_INT64(result);
 }
@@ -198,12 +217,9 @@ pg_stat_get_mod_since_analyze(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->changes_since_analyze);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, changes_since_analyze);
 
        PG_RETURN_INT64(result);
 }
@@ -214,44 +230,58 @@ pg_stat_get_ins_since_vacuum(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->inserts_since_vacuum);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, inserts_since_vacuum);
 
        PG_RETURN_INT64(result);
 }
 
 
 Datum
-pg_stat_get_blocks_fetched(PG_FUNCTION_ARGS)
+pg_stat_get_table_blocks_fetched(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->blocks_fetched);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, blocks_fetched);
+
+       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 = pgstat_fetch_stat_indentry(relid);
+
+       result = PGSTAT_FETCH_STAT_ENTRY(indentry, blocks_fetched);
 
        PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_table_blocks_hit(PG_FUNCTION_ARGS)
+{
+       Oid                     relid = PG_GETARG_OID(0);
+       int64           result;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
+
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, blocks_hit);
+
+       PG_RETURN_INT64(result);
+}
 
 Datum
-pg_stat_get_blocks_hit(PG_FUNCTION_ARGS)
+pg_stat_get_index_blocks_hit(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatIndEntry *indentry = pgstat_fetch_stat_indentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->blocks_hit);
+       result = PGSTAT_FETCH_STAT_ENTRY(indentry, blocks_hit);
 
        PG_RETURN_INT64(result);
 }
@@ -333,12 +363,9 @@ pg_stat_get_vacuum_count(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->vacuum_count);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, vacuum_count);
 
        PG_RETURN_INT64(result);
 }
@@ -348,12 +375,9 @@ pg_stat_get_autovacuum_count(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->autovac_vacuum_count);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, autovac_vacuum_count);
 
        PG_RETURN_INT64(result);
 }
@@ -363,12 +387,9 @@ pg_stat_get_analyze_count(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->analyze_count);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, analyze_count);
 
        PG_RETURN_INT64(result);
 }
@@ -378,12 +399,9 @@ pg_stat_get_autoanalyze_count(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
-       PgStat_StatTabEntry *tabentry;
+       PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-       if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
-               result = 0;
-       else
-               result = (int64) (tabentry->autovac_analyze_count);
+       result = PGSTAT_FETCH_STAT_ENTRY(tabentry, autovac_analyze_count);
 
        PG_RETURN_INT64(result);
 }
@@ -1837,7 +1855,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_table_xact_numscans(PG_FUNCTION_ARGS)
 {
        Oid                     relid = PG_GETARG_OID(0);
        int64           result;
@@ -1851,17 +1869,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 +1904,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 +1997,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 +2012,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 +2136,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..80da09d9e4 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_table_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_table_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_table_lastscan', provolatile => 's', proparallel => 
'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_table_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_table_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_table_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_table_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_table_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_table_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_table_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_table_blocks_hit', provolatile => 's', proparallel 
=> 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_table_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_table_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_table_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..8dae39b917 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;
@@ -497,12 +544,16 @@ extern PgStat_BackendFunctionEntry 
*find_funcstat_entry(Oid func_id);
  * Functions in pgstat_relation.c
  */
 
-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_create_index(Relation rel);
+extern void pgstat_drop_table(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_table_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_table_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 e2c7b59324..761747820b 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -359,11 +359,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
 {
@@ -558,8 +564,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..b339759c82 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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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..07ce31885b 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_table_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_table_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..3d19d1a833 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_table_numscans(c.oid) AS seq_scan,
+    pg_stat_get_table_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_table_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_table_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_table_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_table_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_table_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_table_blocks_fetched(c.oid) - 
pg_stat_get_table_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_table_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_table_blocks_fetched(t.oid) - 
pg_stat_get_table_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_table_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 9683b0a88e..0899cb471e 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