public inbox for [email protected]  
help / color / mirror / Atom feed
Split index and table statistics into different types of stats
17+ messages / 5 participants
[nested] [flat]

* Split index and table statistics into different types of stats
@ 2022-10-31 13:14 Drouvot, Bertrand <[email protected]>
  2022-10-31 16:42 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  0 siblings, 2 replies; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-10-31 13:14 UTC (permalink / raw)
  To: PostgreSQL Hackers <[email protected]>

Hi hackers,

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

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

To sum up:

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

So that the immediate benefits to $SUBJECT are:

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

The attached patch proposal:

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

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

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

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

Looking forward to your feedback,

Regards,

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


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

[2]: 
https://www.postgresql.org/message-id/20220818195124.c7ipzf6c5v7vxymc%40awork3.anarazel.de

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


Attachments:

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


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-10-31 16:42 ` Drouvot, Bertrand <[email protected]>
  1 sibling, 0 replies; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-10-31 16:42 UTC (permalink / raw)
  To: Justin Pryzby <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 10/31/22 2:31 PM, Justin Pryzby wrote:
> I didn't looks closely, but there's a couple places where you wrote
> ";;", which looks unintentional.
> 
> -               PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);
> +               PG_RETURN_TIMESTAMPTZ(tabentry->lastscan);;
> 

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


Attachments:

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


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-01 00:30 ` Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  1 sibling, 1 reply; 17+ messages in thread

From: Andres Freund @ 2022-11-01 00:30 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

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!



> 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.


>  /*
> @@ -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?


> +/*
> + * 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".



> @@ -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.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
@ 2022-11-02 08:58   ` Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-02 08:58 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

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


Attachments:

  [text/plain] v3-0001-split_tables_indexes_stats.patch (100.8K, ../../[email protected]/2-v3-0001-split_tables_indexes_stats.patch)
  download | inline diff:
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


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-04 20:51     ` Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Melanie Plageman @ 2022-11-04 20:51 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: Andres Freund <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi Bertrand,

I'm glad you are working on this.

I had a few thoughts/ideas

It seems better to have all of the counts in the various stats structs
not be prefixed with n_, i_, t_

typedef struct PgStat_StatDBEntry
{
...
    PgStat_Counter n_blocks_fetched;
    PgStat_Counter n_blocks_hit;
    PgStat_Counter n_tuples_returned;
    PgStat_Counter n_tuples_fetched;
...

I've attached a patch (0002) to change this in case you are interested
in making such a change (I've attached all of my suggestions in patches
along with your original patch so that cfbot still passes).

On Wed, Nov 2, 2022 at 5:00 AM Drouvot, Bertrand
<[email protected]> wrote:
> On 11/1/22 1:30 AM, Andres Freund wrote:
> > On 2022-10-31 14:14:15 +0100, Drouvot, Bertrand wrote:
> >> @@ -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.

I looked for other opportunities to de-duplicate, but most of the functions
that were added that are identical except the return type and
PgStat_Kind are short enough that it doesn't make sense to make wrappers
or macros.

I do think it makes sense to reorder the members of the two structs
PgStat_IndexCounts and PgStat_TableCounts so that they have a common
header. I've done that in the attached patch (0003).

In the flush functions, I was also thinking it might be nice to use the
same pattern as is used in [1] and [2] to add the counts together. It
makes the lines a bit easier to read, IMO. If we remove the prefixes
from the count fields, this works for many of the fields. I've attached
a patch (0004) that does something like this, in case you wanted to go
in this direction.

Since you have made new parallel functions for indexes and tables for
many of the functions in pgstat_relation.c, perhaps it makes sense to
split it into pgstat_table.c and pgstat_index.c?

One question I had about the original code (not related to your
refactor) is why the pending stats aren't memset in the flush functions
after aggregating them into the shared stats.

- Melanie

[1] https://github.com/postgres/postgres/blob/master/src/backend/utils/activity/pgstat_checkpointer.c#L4...
[2] https://github.com/postgres/postgres/blob/master/src/backend/utils/activity/pgstat_database.c#L370


Attachments:

  [text/x-patch] v4-0003-reorder-members.patch (1.7K, ../../CAAKRu_asj8kSnukxHV764LTcUz5u-Z+iitjqesDB0yxthSm0Qw@mail.gmail.com/2-v4-0003-reorder-members.patch)
  download | inline diff:
From 507a0d00ff82b7ad356d7565a60024bd38292d46 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Fri, 4 Nov 2022 16:24:30 -0400
Subject: [PATCH v4 3/4] reorder members

---
 src/include/pgstat.h | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 4ec00afc46..eda3540561 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -146,6 +146,13 @@ typedef struct PgStat_BackendSubEntry
 	PgStat_Counter sync_error_count;
 } PgStat_BackendSubEntry;
 
+#define PgStat_RelationCounts \
+	PgStat_Counter numscans; \
+	PgStat_Counter tuples_returned; \
+	PgStat_Counter tuples_fetched; \
+	PgStat_Counter blocks_fetched; \
+	PgStat_Counter blocks_hit;
+
 /* ----------
  * PgStat_IndexCounts			The actual per-index counts kept by a backend
  *
@@ -160,12 +167,7 @@ typedef struct PgStat_BackendSubEntry
  */
 typedef struct PgStat_IndexCounts
 {
-	PgStat_Counter numscans;
-
-	PgStat_Counter tuples_returned;
-	PgStat_Counter tuples_fetched;
-	PgStat_Counter blocks_fetched;
-	PgStat_Counter blocks_hit;
+	PgStat_RelationCounts
 } PgStat_IndexCounts;
 
 /* ----------
@@ -187,10 +189,7 @@ typedef struct PgStat_IndexCounts
  */
 typedef struct PgStat_TableCounts
 {
-	PgStat_Counter numscans;
-
-	PgStat_Counter tuples_returned;
-	PgStat_Counter tuples_fetched;
+	PgStat_RelationCounts
 
 	PgStat_Counter tuples_inserted;
 	PgStat_Counter tuples_updated;
@@ -202,8 +201,6 @@ typedef struct PgStat_TableCounts
 	PgStat_Counter delta_dead_tuples;
 	PgStat_Counter changed_tuples;
 
-	PgStat_Counter blocks_fetched;
-	PgStat_Counter blocks_hit;
 } PgStat_TableCounts;
 
 /* ----------
-- 
2.34.1



  [text/x-patch] v4-0001-Existing-patch-to-split-tab-and-idx.patch (102.3K, ../../CAAKRu_asj8kSnukxHV764LTcUz5u-Z+iitjqesDB0yxthSm0Qw@mail.gmail.com/3-v4-0001-Existing-patch-to-split-tab-and-idx.patch)
  download | inline diff:
From 6c54823efedc9aca94d53a6f70b9b3ab0b94be24 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Fri, 4 Nov 2022 13:49:55 -0400
Subject: [PATCH v4 1/4] Existing patch to split tab and idx

---
 src/backend/access/index/indexam.c            |   2 +-
 src/backend/catalog/heap.c                    |   7 +-
 src/backend/catalog/index.c                   |   4 +-
 src/backend/catalog/system_views.sql          |  62 +--
 src/backend/storage/buffer/bufmgr.c           |  12 +-
 src/backend/utils/activity/pgstat.c           |  28 +-
 src/backend/utils/activity/pgstat_relation.c  | 440 +++++++++++++-----
 src/backend/utils/adt/pgstatfuncs.c           | 244 +++++-----
 src/backend/utils/cache/relcache.c            |   8 +-
 src/include/catalog/pg_proc.dat               |  63 ++-
 src/include/pgstat.h                          | 133 ++++--
 src/include/utils/pgstat_internal.h           |  16 +-
 src/include/utils/rel.h                       |   5 +-
 src/test/isolation/expected/stats.out         |  76 +--
 src/test/isolation/specs/stats.spec           |   4 +-
 src/test/recovery/t/029_stats_restart.pl      |  23 +-
 .../recovery/t/030_stats_cleanup_replica.pl   |   2 +-
 src/test/regress/expected/rules.out           |  48 +-
 src/test/regress/expected/stats.out           | 131 +++++-
 src/test/regress/sql/stats.sql                |  91 +++-
 src/tools/pgindent/typedefs.list              |   3 +-
 21 files changed, 987 insertions(+), 415 deletions(-)

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
-- 
2.34.1



  [text/x-patch] v4-0004-condense-flush-functions.patch (8.3K, ../../CAAKRu_asj8kSnukxHV764LTcUz5u-Z+iitjqesDB0yxthSm0Qw@mail.gmail.com/4-v4-0004-condense-flush-functions.patch)
  download | inline diff:
From 8e6124eb50d09fa09bec07ade2e53fcec27033a3 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Fri, 4 Nov 2022 16:02:30 -0400
Subject: [PATCH v4 4/4] condense flush functions

---
 src/backend/utils/activity/pgstat_relation.c | 153 ++++++++++---------
 1 file changed, 82 insertions(+), 71 deletions(-)

diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 94fbc6faea..d46d58da1e 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -883,83 +883,92 @@ bool
 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_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_Table *) entry_ref->shared_stats;
+	PgStat_StatDBEntry *dbentry;
+	PgStat_StatTabEntry *stats_shmem;
+
+	PgStat_TableCounts *stats_pending =
+		&((PgStat_TableStatus *) entry_ref->pending)->t_counts;
 
 	/*
 	 * Ignore entries that didn't accumulate any actual counts.
 	 */
-	if (memcmp(&lstats->t_counts, &all_zeroes,
+	if (memcmp(stats_pending, &all_zeroes,
 			   sizeof(PgStat_TableCounts)) == 0)
-	{
 		return true;
-	}
 
 	if (!pgstat_lock_entry(entry_ref, nowait))
 		return false;
 
+	stats_shmem = &((PgStatShared_Table *) entry_ref->shared_stats)->stats;
+
 	/* add the values to the shared entry. */
-	tabentry = &shtabstats->stats;
+	stats_shmem->numscans += stats_pending->numscans;
 
-	tabentry->numscans += lstats->t_counts.t_numscans;
-	if (lstats->t_counts.t_numscans)
+	if (stats_pending->numscans)
 	{
 		TimestampTz t = GetCurrentTransactionStopTimestamp();
 
-		if (t > tabentry->lastscan)
-			tabentry->lastscan = t;
+		if (t > stats_shmem->lastscan)
+			stats_shmem->lastscan = t;
 	}
-	tabentry->tuples_returned += lstats->t_counts.t_tuples_returned;
-	tabentry->tuples_fetched += lstats->t_counts.t_tuples_fetched;
-	tabentry->tuples_inserted += lstats->t_counts.t_tuples_inserted;
-	tabentry->tuples_updated += lstats->t_counts.t_tuples_updated;
-	tabentry->tuples_deleted += lstats->t_counts.t_tuples_deleted;
-	tabentry->tuples_hot_updated += lstats->t_counts.t_tuples_hot_updated;
+
+#define PGSTAT_ACCUM_TAB(fld) \
+	(stats_shmem)->fld += (stats_pending)->fld
+
+	PGSTAT_ACCUM_TAB(tuples_returned);
+	PGSTAT_ACCUM_TAB(tuples_fetched);
+	PGSTAT_ACCUM_TAB(tuples_inserted);
+	PGSTAT_ACCUM_TAB(tuples_updated);
+	PGSTAT_ACCUM_TAB(tuples_deleted);
+	PGSTAT_ACCUM_TAB(tuples_hot_updated);
+	PGSTAT_ACCUM_TAB(blocks_fetched);
+	PGSTAT_ACCUM_TAB(blocks_hit);
+
+#undef PGSTAT_ACCUM_TAB
 
 	/*
 	 * If table was truncated/dropped, first reset the live/dead counters.
 	 */
-	if (lstats->t_counts.t_truncdropped)
+	if (stats_pending->truncdropped)
 	{
-		tabentry->n_live_tuples = 0;
-		tabentry->n_dead_tuples = 0;
-		tabentry->inserts_since_vacuum = 0;
+		stats_shmem->live_tuples          = 0;
+		stats_shmem->dead_tuples          = 0;
+		stats_shmem->inserts_since_vacuum = 0;
 	}
 
-	tabentry->n_live_tuples += lstats->t_counts.t_delta_live_tuples;
-	tabentry->n_dead_tuples += lstats->t_counts.t_delta_dead_tuples;
-	tabentry->changes_since_analyze += lstats->t_counts.t_changed_tuples;
-	tabentry->inserts_since_vacuum += lstats->t_counts.t_tuples_inserted;
-	tabentry->blocks_fetched += lstats->t_counts.t_blocks_fetched;
-	tabentry->blocks_hit += lstats->t_counts.t_blocks_hit;
+	stats_shmem->live_tuples += stats_pending->delta_live_tuples;
+	stats_shmem->dead_tuples += stats_pending->delta_dead_tuples;
+	stats_shmem->inserts_since_vacuum += stats_pending->tuples_inserted;
+	stats_shmem->changes_since_analyze += stats_pending->changed_tuples;
 
-	/* Clamp n_live_tuples in case of negative delta_live_tuples */
-	tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
-	/* Likewise for n_dead_tuples */
-	tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
+	/* Clamp live_tuples in case of negative delta_live_tuples */
+	stats_shmem->live_tuples = Max(stats_shmem->live_tuples, 0);
+	/* Likewise for dead_tuples */
+	stats_shmem->dead_tuples = Max(stats_shmem->dead_tuples, 0);
 
 	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->t_counts.t_tuples_returned;
-	dbentry->n_tuples_fetched += lstats->t_counts.t_tuples_fetched;
-	dbentry->n_tuples_inserted += lstats->t_counts.t_tuples_inserted;
-	dbentry->n_tuples_updated += lstats->t_counts.t_tuples_updated;
-	dbentry->n_tuples_deleted += lstats->t_counts.t_tuples_deleted;
-	dbentry->n_blocks_fetched += lstats->t_counts.t_blocks_fetched;
-	dbentry->n_blocks_hit += lstats->t_counts.t_blocks_hit;
+	dbentry = pgstat_prep_database_pending(entry_ref->shared_entry->key.dboid);
+
+#define PGSTAT_ACCUM_TAB(fld) \
+	(dbentry)->fld += (stats_pending)->fld
+
+	PGSTAT_ACCUM_TAB(tuples_returned);
+	PGSTAT_ACCUM_TAB(tuples_fetched);
+	PGSTAT_ACCUM_TAB(tuples_inserted);
+	PGSTAT_ACCUM_TAB(tuples_updated);
+	PGSTAT_ACCUM_TAB(tuples_deleted);
+	PGSTAT_ACCUM_TAB(blocks_fetched);
+	PGSTAT_ACCUM_TAB(blocks_hit);
+
+#undef PGSTAT_ACCUM_TAB
 
 	return true;
 }
 
+
 /*
  * Flush out pending stats for the entry
  *
@@ -973,59 +982,61 @@ 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 */
+	PgStat_StatDBEntry *dbentry;
+	PgStat_StatIndEntry *stats_shmem;
 
-	dboid = entry_ref->shared_entry->key.dboid;
-	lstats = (PgStat_IndexStatus *) entry_ref->pending;
-	shrelcomstats = (PgStatShared_Index *) entry_ref->shared_stats;
+	PgStat_IndexCounts *stats_pending =
+		&((PgStat_IndexStatus *) entry_ref->pending)->i_counts;
 
 	/*
 	 * 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,
+	if (memcmp(stats_pending, &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;
+	stats_shmem = &((PgStatShared_Index *) entry_ref->shared_stats)->stats;
 
-	indentry->numscans += lstats->i_counts.i_numscans;
+	/* add the values to the shared entry. */
+	stats_shmem->numscans += stats_pending->numscans;
 
-	if (lstats->i_counts.i_numscans)
+	if (stats_pending->numscans)
 	{
 		TimestampTz t = GetCurrentTransactionStopTimestamp();
 
-		if (t > indentry->lastscan)
-			indentry->lastscan = t;
+		if (t > stats_shmem->lastscan)
+			stats_shmem->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;
+
+#define PGSTAT_ACCUM_IDX(fld) \
+	(stats_shmem)->fld += (stats_pending)->fld
+	PGSTAT_ACCUM_IDX(tuples_returned);
+	PGSTAT_ACCUM_IDX(tuples_fetched);
+	PGSTAT_ACCUM_IDX(blocks_fetched);
+	PGSTAT_ACCUM_IDX(blocks_hit);
+#undef PGSTAT_ACCUM_IDX
 
 	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;
+	dbentry = pgstat_prep_database_pending(entry_ref->shared_entry->key.dboid);
+
+#define PGSTAT_ACCUM_IDX(fld) \
+	(dbentry)->fld += (stats_pending)->fld
+	PGSTAT_ACCUM_IDX(tuples_returned);
+	PGSTAT_ACCUM_IDX(tuples_fetched);
+	PGSTAT_ACCUM_IDX(blocks_fetched);
+	PGSTAT_ACCUM_IDX(blocks_hit);
+#undef PGSTAT_ACCUM_IDX
 
 	return true;
 }
 
+
 void
 pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref)
 {
-- 
2.34.1



  [text/x-patch] v4-0002-Remove-n-i-t_-prefixes-from-stats-structs.patch (21.7K, ../../CAAKRu_asj8kSnukxHV764LTcUz5u-Z+iitjqesDB0yxthSm0Qw@mail.gmail.com/5-v4-0002-Remove-n-i-t_-prefixes-from-stats-structs.patch)
  download | inline diff:
From 8912daf3bccbff719f8ebb03448e43281f67b28d Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Fri, 4 Nov 2022 15:48:10 -0400
Subject: [PATCH v4 2/4] Remove n/i/t_ prefixes from stats structs

---
 src/backend/access/heap/README.HOT           |  2 +-
 src/backend/postmaster/autovacuum.c          |  2 +-
 src/backend/utils/activity/pgstat_database.c | 16 ++---
 src/backend/utils/activity/pgstat_relation.c | 72 +++++++++----------
 src/backend/utils/adt/pgstatfuncs.c          | 42 +++++------
 src/include/pgstat.h                         | 76 ++++++++++----------
 6 files changed, 105 insertions(+), 105 deletions(-)

diff --git a/src/backend/access/heap/README.HOT b/src/backend/access/heap/README.HOT
index 68c6709aa8..6fd1767f70 100644
--- a/src/backend/access/heap/README.HOT
+++ b/src/backend/access/heap/README.HOT
@@ -271,7 +271,7 @@ physical tuple by eliminating an intermediate heap-only tuple or
 replacing a physical root tuple by a redirect pointer, a decrement in
 the table's number of dead tuples is reported to pgstats, which may
 postpone autovacuuming.  Note that we do not count replacing a root tuple
-by a DEAD line pointer as decrementing n_dead_tuples; we still want
+by a DEAD line pointer as decrementing dead_tuples; we still want
 autovacuum to run to clean up the index entries and DEAD item.
 
 This area probably needs further work ...
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 601834d4b4..2a295ce884 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -3081,7 +3081,7 @@ relation_needs_vacanalyze(Oid relid,
 	if (PointerIsValid(tabentry) && AutoVacuumingActive())
 	{
 		reltuples = classForm->reltuples;
-		vactuples = tabentry->n_dead_tuples;
+		vactuples = tabentry->dead_tuples;
 		instuples = tabentry->inserts_since_vacuum;
 		anltuples = tabentry->changes_since_analyze;
 
diff --git a/src/backend/utils/activity/pgstat_database.c b/src/backend/utils/activity/pgstat_database.c
index d9275611f0..ce8a9f4ac7 100644
--- a/src/backend/utils/activity/pgstat_database.c
+++ b/src/backend/utils/activity/pgstat_database.c
@@ -372,14 +372,14 @@ pgstat_database_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 
 	PGSTAT_ACCUM_DBCOUNT(n_xact_commit);
 	PGSTAT_ACCUM_DBCOUNT(n_xact_rollback);
-	PGSTAT_ACCUM_DBCOUNT(n_blocks_fetched);
-	PGSTAT_ACCUM_DBCOUNT(n_blocks_hit);
-
-	PGSTAT_ACCUM_DBCOUNT(n_tuples_returned);
-	PGSTAT_ACCUM_DBCOUNT(n_tuples_fetched);
-	PGSTAT_ACCUM_DBCOUNT(n_tuples_inserted);
-	PGSTAT_ACCUM_DBCOUNT(n_tuples_updated);
-	PGSTAT_ACCUM_DBCOUNT(n_tuples_deleted);
+	PGSTAT_ACCUM_DBCOUNT(blocks_fetched);
+	PGSTAT_ACCUM_DBCOUNT(blocks_hit);
+
+	PGSTAT_ACCUM_DBCOUNT(tuples_returned);
+	PGSTAT_ACCUM_DBCOUNT(tuples_fetched);
+	PGSTAT_ACCUM_DBCOUNT(tuples_inserted);
+	PGSTAT_ACCUM_DBCOUNT(tuples_updated);
+	PGSTAT_ACCUM_DBCOUNT(tuples_deleted);
 
 	/* last_autovac_time is reported immediately */
 	Assert(pendingent->last_autovac_time == 0);
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_relation.c
index 13cdd7f849..94fbc6faea 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -294,8 +294,8 @@ pgstat_report_vacuum(Oid tableoid, bool shared,
 	shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
 	tabentry = &shtabentry->stats;
 
-	tabentry->n_live_tuples = livetuples;
-	tabentry->n_dead_tuples = deadtuples;
+	tabentry->live_tuples = livetuples;
+	tabentry->dead_tuples = deadtuples;
 
 	/*
 	 * It is quite possible that a non-aggressive VACUUM ended up skipping
@@ -365,7 +365,7 @@ pgstat_report_analyze(Relation rel,
 			deadtuples -= trans->tuples_updated + trans->tuples_deleted;
 		}
 		/* count stuff inserted by already-aborted subxacts, too */
-		deadtuples -= rel->pgstattab_info->t_counts.t_delta_dead_tuples;
+		deadtuples -= rel->pgstattab_info->t_counts.delta_dead_tuples;
 		/* Since ANALYZE's counts are estimates, we could have underflowed */
 		livetuples = Max(livetuples, 0);
 		deadtuples = Max(deadtuples, 0);
@@ -381,8 +381,8 @@ pgstat_report_analyze(Relation rel,
 	shtabentry = (PgStatShared_Table *) entry_ref->shared_stats;
 	tabentry = &shtabentry->stats;
 
-	tabentry->n_live_tuples = livetuples;
-	tabentry->n_dead_tuples = deadtuples;
+	tabentry->live_tuples = livetuples;
+	tabentry->dead_tuples = deadtuples;
 
 	/*
 	 * If commanded, reset changes_since_analyze to zero.  This forgets any
@@ -434,9 +434,9 @@ pgstat_count_heap_update(Relation rel, bool hot)
 		ensure_tabstat_xact_level(pgstattab_info);
 		pgstattab_info->trans->tuples_updated++;
 
-		/* t_tuples_hot_updated is nontransactional, so just advance it */
+		/* tuples_hot_updated is nontransactional, so just advance it */
 		if (hot)
-			pgstattab_info->t_counts.t_tuples_hot_updated++;
+			pgstattab_info->t_counts.tuples_hot_updated++;
 	}
 }
 
@@ -477,7 +477,7 @@ pgstat_count_truncate(Relation rel)
  * update dead-tuples count
  *
  * The semantics of this are that we are reporting the nontransactional
- * recovery of "delta" dead tuples; so t_delta_dead_tuples decreases
+ * recovery of "delta" dead tuples; so delta_dead_tuples decreases
  * rather than increasing, and the change goes straight into the per-table
  * counter, not into transactional state.
  */
@@ -488,7 +488,7 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
 	{
 		PgStat_TableStatus *pgstattab_info = rel->pgstattab_info;
 
-		pgstattab_info->t_counts.t_delta_dead_tuples -= delta;
+		pgstattab_info->t_counts.delta_dead_tuples -= delta;
 	}
 }
 
@@ -631,33 +631,33 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 		if (!isCommit)
 			restore_truncdrop_counters(trans);
 		/* count attempted actions regardless of commit/abort */
-		tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
-		tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
-		tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+		tabstat->t_counts.tuples_inserted += trans->tuples_inserted;
+		tabstat->t_counts.tuples_updated += trans->tuples_updated;
+		tabstat->t_counts.tuples_deleted += trans->tuples_deleted;
 		if (isCommit)
 		{
-			tabstat->t_counts.t_truncdropped = trans->truncdropped;
+			tabstat->t_counts.truncdropped = trans->truncdropped;
 			if (trans->truncdropped)
 			{
 				/* forget live/dead stats seen by backend thus far */
-				tabstat->t_counts.t_delta_live_tuples = 0;
-				tabstat->t_counts.t_delta_dead_tuples = 0;
+				tabstat->t_counts.delta_live_tuples = 0;
+				tabstat->t_counts.delta_dead_tuples = 0;
 			}
 			/* insert adds a live tuple, delete removes one */
-			tabstat->t_counts.t_delta_live_tuples +=
+			tabstat->t_counts.delta_live_tuples +=
 				trans->tuples_inserted - trans->tuples_deleted;
 			/* update and delete each create a dead tuple */
-			tabstat->t_counts.t_delta_dead_tuples +=
+			tabstat->t_counts.delta_dead_tuples +=
 				trans->tuples_updated + trans->tuples_deleted;
 			/* insert, update, delete each count as one change event */
-			tabstat->t_counts.t_changed_tuples +=
+			tabstat->t_counts.changed_tuples +=
 				trans->tuples_inserted + trans->tuples_updated +
 				trans->tuples_deleted;
 		}
 		else
 		{
 			/* inserted tuples are dead, deleted tuples are unaffected */
-			tabstat->t_counts.t_delta_dead_tuples +=
+			tabstat->t_counts.delta_dead_tuples +=
 				trans->tuples_inserted + trans->tuples_updated;
 			/* an aborted xact generates no changed_tuple events */
 		}
@@ -737,11 +737,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 			/* first restore values obliterated by truncate/drop */
 			restore_truncdrop_counters(trans);
 			/* count attempted actions regardless of commit/abort */
-			tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
-			tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
-			tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
+			tabstat->t_counts.tuples_inserted += trans->tuples_inserted;
+			tabstat->t_counts.tuples_updated += trans->tuples_updated;
+			tabstat->t_counts.tuples_deleted += trans->tuples_deleted;
 			/* inserted tuples are dead, deleted tuples are unaffected */
-			tabstat->t_counts.t_delta_dead_tuples +=
+			tabstat->t_counts.delta_dead_tuples +=
 				trans->tuples_inserted + trans->tuples_updated;
 			tabstat->trans = trans->upper;
 			pfree(trans);
@@ -821,21 +821,21 @@ pgstat_twophase_postcommit(TransactionId xid, uint16 info,
 	pgstattab_info = pgstat_prep_table_pending(rec->t_id, rec->t_shared);
 
 	/* Same math as in AtEOXact_PgStat, commit case */
-	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;
+	pgstattab_info->t_counts.tuples_inserted += rec->tuples_inserted;
+	pgstattab_info->t_counts.tuples_updated += rec->tuples_updated;
+	pgstattab_info->t_counts.tuples_deleted += rec->tuples_deleted;
+	pgstattab_info->t_counts.truncdropped = rec->t_truncdropped;
 	if (rec->t_truncdropped)
 	{
 		/* forget live/dead stats seen by backend thus far */
-		pgstattab_info->t_counts.t_delta_live_tuples = 0;
-		pgstattab_info->t_counts.t_delta_dead_tuples = 0;
+		pgstattab_info->t_counts.delta_live_tuples = 0;
+		pgstattab_info->t_counts.delta_dead_tuples = 0;
 	}
-	pgstattab_info->t_counts.t_delta_live_tuples +=
+	pgstattab_info->t_counts.delta_live_tuples +=
 		rec->tuples_inserted - rec->tuples_deleted;
-	pgstattab_info->t_counts.t_delta_dead_tuples +=
+	pgstattab_info->t_counts.delta_dead_tuples +=
 		rec->tuples_updated + rec->tuples_deleted;
-	pgstattab_info->t_counts.t_changed_tuples +=
+	pgstattab_info->t_counts.changed_tuples +=
 		rec->tuples_inserted + rec->tuples_updated +
 		rec->tuples_deleted;
 }
@@ -863,10 +863,10 @@ pgstat_twophase_postabort(TransactionId xid, uint16 info,
 		rec->tuples_updated = rec->updated_pre_truncdrop;
 		rec->tuples_deleted = rec->deleted_pre_truncdrop;
 	}
-	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 +=
+	pgstattab_info->t_counts.tuples_inserted += rec->tuples_inserted;
+	pgstattab_info->t_counts.tuples_updated += rec->tuples_updated;
+	pgstattab_info->t_counts.tuples_deleted += rec->tuples_deleted;
+	pgstattab_info->t_counts.delta_dead_tuples +=
 		rec->tuples_inserted + rec->tuples_updated;
 }
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 0dd6148ed1..17e044b90a 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -193,7 +193,7 @@ pg_stat_get_live_tuples(PG_FUNCTION_ARGS)
 	int64		result;
 	PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-	result = PGSTAT_FETCH_STAT_ENTRY(tabentry, n_live_tuples);
+	result = PGSTAT_FETCH_STAT_ENTRY(tabentry, live_tuples);
 
 	PG_RETURN_INT64(result);
 }
@@ -206,7 +206,7 @@ pg_stat_get_dead_tuples(PG_FUNCTION_ARGS)
 	int64		result;
 	PgStat_StatTabEntry *tabentry = pgstat_fetch_stat_tabentry(relid);
 
-	result = PGSTAT_FETCH_STAT_ENTRY(tabentry, n_dead_tuples);
+	result = PGSTAT_FETCH_STAT_ENTRY(tabentry, dead_tuples);
 
 	PG_RETURN_INT64(result);
 }
@@ -1249,7 +1249,7 @@ pg_stat_get_db_blocks_fetched(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_blocks_fetched);
+		result = (int64) (dbentry->blocks_fetched);
 
 	PG_RETURN_INT64(result);
 }
@@ -1265,7 +1265,7 @@ pg_stat_get_db_blocks_hit(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_blocks_hit);
+		result = (int64) (dbentry->blocks_hit);
 
 	PG_RETURN_INT64(result);
 }
@@ -1281,7 +1281,7 @@ pg_stat_get_db_tuples_returned(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_tuples_returned);
+		result = (int64) (dbentry->tuples_returned);
 
 	PG_RETURN_INT64(result);
 }
@@ -1297,7 +1297,7 @@ pg_stat_get_db_tuples_fetched(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_tuples_fetched);
+		result = (int64) (dbentry->tuples_fetched);
 
 	PG_RETURN_INT64(result);
 }
@@ -1313,7 +1313,7 @@ pg_stat_get_db_tuples_inserted(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_tuples_inserted);
+		result = (int64) (dbentry->tuples_inserted);
 
 	PG_RETURN_INT64(result);
 }
@@ -1329,7 +1329,7 @@ pg_stat_get_db_tuples_updated(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_tuples_updated);
+		result = (int64) (dbentry->tuples_updated);
 
 	PG_RETURN_INT64(result);
 }
@@ -1345,7 +1345,7 @@ pg_stat_get_db_tuples_deleted(PG_FUNCTION_ARGS)
 	if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (dbentry->n_tuples_deleted);
+		result = (int64) (dbentry->tuples_deleted);
 
 	PG_RETURN_INT64(result);
 }
@@ -1864,7 +1864,7 @@ pg_stat_get_table_xact_numscans(PG_FUNCTION_ARGS)
 	if ((tabentry = find_tabstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (tabentry->t_counts.t_numscans);
+		result = (int64) (tabentry->t_counts.numscans);
 
 	PG_RETURN_INT64(result);
 }
@@ -1879,7 +1879,7 @@ pg_stat_get_index_xact_numscans(PG_FUNCTION_ARGS)
 	if ((indentry = find_indstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (indentry->i_counts.i_numscans);
+		result = (int64) (indentry->i_counts.numscans);
 
 	PG_RETURN_INT64(result);
 }
@@ -1894,7 +1894,7 @@ pg_stat_get_xact_tuples_returned(PG_FUNCTION_ARGS)
 	if ((indentry = find_indstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (indentry->i_counts.i_tuples_returned);
+		result = (int64) (indentry->i_counts.tuples_returned);
 
 	PG_RETURN_INT64(result);
 }
@@ -1909,7 +1909,7 @@ pg_stat_get_xact_tuples_fetched(PG_FUNCTION_ARGS)
 	if ((indentry = find_indstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (indentry->i_counts.i_tuples_fetched);
+		result = (int64) (indentry->i_counts.tuples_fetched);
 
 	PG_RETURN_INT64(result);
 }
@@ -1926,8 +1926,8 @@ pg_stat_get_xact_tuples_inserted(PG_FUNCTION_ARGS)
 		result = 0;
 	else
 	{
-		result = tabentry->t_counts.t_tuples_inserted;
-		/* live subtransactions' counts aren't in t_tuples_inserted yet */
+		result = tabentry->t_counts.tuples_inserted;
+		/* live subtransactions' counts aren't in tuples_inserted yet */
 		for (trans = tabentry->trans; trans != NULL; trans = trans->upper)
 			result += trans->tuples_inserted;
 	}
@@ -1947,8 +1947,8 @@ pg_stat_get_xact_tuples_updated(PG_FUNCTION_ARGS)
 		result = 0;
 	else
 	{
-		result = tabentry->t_counts.t_tuples_updated;
-		/* live subtransactions' counts aren't in t_tuples_updated yet */
+		result = tabentry->t_counts.tuples_updated;
+		/* live subtransactions' counts aren't in tuples_updated yet */
 		for (trans = tabentry->trans; trans != NULL; trans = trans->upper)
 			result += trans->tuples_updated;
 	}
@@ -1968,7 +1968,7 @@ pg_stat_get_xact_tuples_deleted(PG_FUNCTION_ARGS)
 		result = 0;
 	else
 	{
-		result = tabentry->t_counts.t_tuples_deleted;
+		result = tabentry->t_counts.tuples_deleted;
 		/* live subtransactions' counts aren't in t_tuples_deleted yet */
 		for (trans = tabentry->trans; trans != NULL; trans = trans->upper)
 			result += trans->tuples_deleted;
@@ -1987,7 +1987,7 @@ pg_stat_get_xact_tuples_hot_updated(PG_FUNCTION_ARGS)
 	if ((tabentry = find_tabstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (tabentry->t_counts.t_tuples_hot_updated);
+		result = (int64) (tabentry->t_counts.tuples_hot_updated);
 
 	PG_RETURN_INT64(result);
 }
@@ -2002,7 +2002,7 @@ pg_stat_get_xact_blocks_fetched(PG_FUNCTION_ARGS)
 	if ((indentry = find_indstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (indentry->i_counts.i_blocks_fetched);
+		result = (int64) (indentry->i_counts.blocks_fetched);
 
 	PG_RETURN_INT64(result);
 }
@@ -2017,7 +2017,7 @@ pg_stat_get_xact_blocks_hit(PG_FUNCTION_ARGS)
 	if ((indentry = find_indstat_entry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (indentry->i_counts.i_blocks_hit);
+		result = (int64) (indentry->i_counts.blocks_hit);
 
 	PG_RETURN_INT64(result);
 }
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 8dae39b917..4ec00afc46 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -160,12 +160,12 @@ typedef struct PgStat_BackendSubEntry
  */
 typedef struct PgStat_IndexCounts
 {
-	PgStat_Counter i_numscans;
+	PgStat_Counter numscans;
 
-	PgStat_Counter i_tuples_returned;
-	PgStat_Counter i_tuples_fetched;
-	PgStat_Counter i_blocks_fetched;
-	PgStat_Counter i_blocks_hit;
+	PgStat_Counter tuples_returned;
+	PgStat_Counter tuples_fetched;
+	PgStat_Counter blocks_fetched;
+	PgStat_Counter blocks_hit;
 } PgStat_IndexCounts;
 
 /* ----------
@@ -187,23 +187,23 @@ typedef struct PgStat_IndexCounts
  */
 typedef struct PgStat_TableCounts
 {
-	PgStat_Counter t_numscans;
+	PgStat_Counter numscans;
 
-	PgStat_Counter t_tuples_returned;
-	PgStat_Counter t_tuples_fetched;
+	PgStat_Counter tuples_returned;
+	PgStat_Counter tuples_fetched;
 
-	PgStat_Counter t_tuples_inserted;
-	PgStat_Counter t_tuples_updated;
-	PgStat_Counter t_tuples_deleted;
-	PgStat_Counter t_tuples_hot_updated;
-	bool		t_truncdropped;
+	PgStat_Counter tuples_inserted;
+	PgStat_Counter tuples_updated;
+	PgStat_Counter tuples_deleted;
+	PgStat_Counter tuples_hot_updated;
+	bool		truncdropped;
 
-	PgStat_Counter t_delta_live_tuples;
-	PgStat_Counter t_delta_dead_tuples;
-	PgStat_Counter t_changed_tuples;
+	PgStat_Counter delta_live_tuples;
+	PgStat_Counter delta_dead_tuples;
+	PgStat_Counter changed_tuples;
 
-	PgStat_Counter t_blocks_fetched;
-	PgStat_Counter t_blocks_hit;
+	PgStat_Counter blocks_fetched;
+	PgStat_Counter blocks_hit;
 } PgStat_TableCounts;
 
 /* ----------
@@ -316,14 +316,14 @@ typedef struct PgStat_StatDBEntry
 {
 	PgStat_Counter n_xact_commit;
 	PgStat_Counter n_xact_rollback;
-	PgStat_Counter n_blocks_fetched;
-	PgStat_Counter n_blocks_hit;
-	PgStat_Counter n_tuples_returned;
-	PgStat_Counter n_tuples_fetched;
-	PgStat_Counter n_tuples_inserted;
-	PgStat_Counter n_tuples_updated;
-	PgStat_Counter n_tuples_deleted;
-	TimestampTz last_autovac_time;
+	PgStat_Counter blocks_fetched;
+	PgStat_Counter blocks_hit;
+	PgStat_Counter tuples_returned;
+	PgStat_Counter tuples_fetched;
+	PgStat_Counter tuples_inserted;
+	PgStat_Counter tuples_updated;
+	PgStat_Counter tuples_deleted;
+	TimestampTz    last_autovac_time;
 	PgStat_Counter n_conflict_tablespace;
 	PgStat_Counter n_conflict_lock;
 	PgStat_Counter n_conflict_snapshot;
@@ -400,8 +400,8 @@ typedef struct PgStat_StatTabEntry
 	PgStat_Counter tuples_deleted;
 	PgStat_Counter tuples_hot_updated;
 
-	PgStat_Counter n_live_tuples;
-	PgStat_Counter n_dead_tuples;
+	PgStat_Counter live_tuples;
+	PgStat_Counter dead_tuples;
 	PgStat_Counter changes_since_analyze;
 	PgStat_Counter inserts_since_vacuum;
 
@@ -580,52 +580,52 @@ extern void pgstat_report_analyze(Relation rel,
 #define pgstat_count_heap_scan(rel)									\
 	do {															\
 		if (pgstat_should_count_table(rel))						\
-			(rel)->pgstattab_info->t_counts.t_numscans++;				\
+			(rel)->pgstattab_info->t_counts.numscans++;				\
 	} while (0)
 #define pgstat_count_heap_getnext(rel)								\
 	do {															\
 		if (pgstat_should_count_table(rel))						\
-			(rel)->pgstattab_info->t_counts.t_tuples_returned++;		\
+			(rel)->pgstattab_info->t_counts.tuples_returned++;		\
 	} while (0)
 #define pgstat_count_heap_fetch(rel)								\
 	do {															\
 		if (pgstat_should_count_table(rel))						\
-			(rel)->pgstattab_info->t_counts.t_tuples_fetched++;		\
+			(rel)->pgstattab_info->t_counts.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++;		\
+			(rel)->pgstatind_info->i_counts.tuples_fetched++;		\
 	} while (0)
 #define pgstat_count_index_scan(rel)								\
 	do {															\
 		if (pgstat_should_count_index(rel))						\
-			(rel)->pgstatind_info->i_counts.i_numscans++;			\
+			(rel)->pgstatind_info->i_counts.numscans++;			\
 	} while (0)
 #define pgstat_count_index_tuples(rel, n)							\
 	do {															\
 		if (pgstat_should_count_index(rel))						\
-			(rel)->pgstatind_info->i_counts.i_tuples_returned += (n);	\
+			(rel)->pgstatind_info->i_counts.tuples_returned += (n);	\
 	} while (0)
 #define pgstat_count_table_buffer_read(rel)								\
 	do {															\
 		if (pgstat_should_count_table(rel))	\
-			(rel)->pgstattab_info->t_counts.t_blocks_fetched++;		\
+			(rel)->pgstattab_info->t_counts.blocks_fetched++;		\
 	} while (0)
 #define pgstat_count_index_buffer_read(rel)								\
 	do {															\
 		if (pgstat_should_count_index(rel))	\
-			(rel)->pgstatind_info->i_counts.i_blocks_fetched++;		\
+			(rel)->pgstatind_info->i_counts.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++; \
+			(rel)->pgstattab_info->t_counts.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++; \
+			(rel)->pgstatind_info->i_counts.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);
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
@ 2022-11-14 10:36       ` Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-14 10:36 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: Andres Freund <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/4/22 9:51 PM, Melanie Plageman wrote:
> Hi Bertrand,
> 
> I'm glad you are working on this.
> 
> I had a few thoughts/ideas
> 

Thanks for looking at it!

> It seems better to have all of the counts in the various stats structs
> not be prefixed with n_, i_, t_
> 
> typedef struct PgStat_StatDBEntry
> {
> ...
>      PgStat_Counter n_blocks_fetched;
>      PgStat_Counter n_blocks_hit;
>      PgStat_Counter n_tuples_returned;
>      PgStat_Counter n_tuples_fetched;
> ...
> 
> I've attached a patch (0002) to change this in case you are interested
> in making such a change

I did not renamed initially the fields/columns to ease the review.

Indeed, I think we should go further than removing the n_, i_ and t_ 
prefixes so that the fields actually match the view's columns.

For example, currently pg_stat_all_indexes.idx_tup_read is linked to 
"tuples_returned", so that it would make sense to rename 
"tuples_returned" to "tuples_read" or even "tup_read" in the indexes 
counters.

That's why I had in mind to do this fields/columns renaming into a 
separate patch (once this one is committed), so that the current one 
focus only on splitting the stats: what do you think?

> (I've attached all of my suggestions in patches
> along with your original patch so that cfbot still passes).
> 
> On Wed, Nov 2, 2022 at 5:00 AM Drouvot, Bertrand
> <[email protected]> wrote:
>> On 11/1/22 1:30 AM, Andres Freund wrote:
>>> On 2022-10-31 14:14:15 +0100, Drouvot, Bertrand wrote:
>>>> @@ -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.
> 
> I looked for other opportunities to de-duplicate, but most of the functions
> that were added that are identical except the return type and
> PgStat_Kind are short enough that it doesn't make sense to make wrappers
> or macros.
> 

Yeah, agree.

> I do think it makes sense to reorder the members of the two structs
> PgStat_IndexCounts and PgStat_TableCounts so that they have a common
> header. I've done that in the attached patch (0003).
> 

That's a good idea, thanks! But for that we would need to have the same 
fields names, means:

- Remove the prefixes (as you've done in 0002)
- And probably reduce the number of fields in the new 
PgStat_RelationCounts that 003 is introducing (for example 
tuples_returned should be excluded if we're going to rename it later on 
to "tuples_read" for the indexes to map the 
pg_stat_all_indexes.idx_tup_read column).

ISTM that we should do it in the "renaming" effort, after this patch is 
committed.

> In the flush functions, I was also thinking it might be nice to use the
> same pattern as is used in [1] and [2] to add the counts together. It
> makes the lines a bit easier to read, IMO. If we remove the prefixes
> from the count fields, this works for many of the fields. I've attached
> a patch (0004) that does something like this, in case you wanted to go
> in this direction.

I like it too but same remarks as previously. I think it should be part 
of the "renaming" effort.

> 
> Since you have made new parallel functions for indexes and tables for
> many of the functions in pgstat_relation.c, perhaps it makes sense to
> split it into pgstat_table.c and pgstat_index.c?

Good point, thanks, I'll work on it.

> 
> One question I had about the original code (not related to your
> refactor) is why the pending stats aren't memset in the flush functions
> after aggregating them into the shared stats.

Not sure I'm getting your point, do you think something is not right 
with the flush functions?

Regards,

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-15 09:48         ` Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-15 09:48 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: Andres Freund <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/14/22 11:36 AM, Drouvot, Bertrand wrote:
> On 11/4/22 9:51 PM, Melanie Plageman wrote:
>> Since you have made new parallel functions for indexes and tables for
>> many of the functions in pgstat_relation.c, perhaps it makes sense to
>> split it into pgstat_table.c and pgstat_index.c?
> 
> Good point, thanks, I'll work on it.
> 

Please find attached v4 adding pgstat_table.c and pgstat_index.c (and 
removing pgstat_relation.c).

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..e07d8dbf21
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,306 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 76%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index 55a355f583..26d47abee1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -43,74 +43,47 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -118,89 +91,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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 +188,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 +200,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 +247,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 +266,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 +324,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 +339,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 +358,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 +373,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 +396,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 +436,7 @@ 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);
 }
 
 /*
@@ -476,9 +452,9 @@ 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;
@@ -486,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -494,7 +470,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -545,14 +521,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -629,11 +605,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -668,10 +644,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -694,27 +670,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 +706,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 +718,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 +735,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 +767,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;
 	}
@@ -840,12 +816,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -853,12 +829,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -872,7 +848,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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 +864,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae3365d917..be7f175bf1 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);
 	TimestampTz result;
@@ -70,50 +80,80 @@ pg_stat_get_lastscan(PG_FUNCTION_ARGS)
 		PG_RETURN_TIMESTAMPTZ(result);
 }
 
-
 Datum
-pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
+pg_stat_get_index_lastscan(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
-	int64		result;
-	PgStat_StatTabEntry *tabentry;
+	TimestampTz result;
+	PgStat_StatIndEntry *indentry;
 
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (tabentry->tuples_returned);
+		result = indentry->lastscan;
+
+	if (result == 0)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TIMESTAMPTZ(result);
+}
+
+Datum
+pg_stat_get_table_tuples_returned(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, 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);
+}
 
 Datum
 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);
 }
@@ -124,12 +164,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);
 }
@@ -140,12 +177,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);
 }
@@ -156,12 +190,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);
 }
@@ -172,12 +203,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);
 }
@@ -188,12 +216,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);
 }
@@ -204,12 +229,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);
 }
@@ -220,44 +242,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);
 }
@@ -339,12 +375,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);
 }
@@ -354,12 +387,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);
 }
@@ -369,12 +399,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);
 }
@@ -384,12 +411,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);
 }
@@ -1843,7 +1867,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;
@@ -1857,17 +1881,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);
 }
@@ -1877,12 +1916,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);
 }
@@ -1970,12 +2009,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);
 }
@@ -1985,12 +2024,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);
 }
@@ -2109,7 +2148,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..da9e60225f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2720,8 +2723,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 +6318,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 9dbe9ec801..8adbade072 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..149394c7ef 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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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 1d84407a03..e3f1ae7821 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;$$;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..12ec87a6d1 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
 ---
@@ -493,40 +564,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 f8302f1ed1..898f09206a 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


Attachments:

  [text/plain] v4-0001-split_tables_indexes_stats.patch (111.6K, ../../[email protected]/2-v4-0001-split_tables_indexes_stats.patch)
  download | inline diff:
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..e07d8dbf21
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,306 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 76%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index 55a355f583..26d47abee1 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -43,74 +43,47 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -118,89 +91,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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 +188,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 +200,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 +247,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 +266,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 +324,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 +339,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 +358,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 +373,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 +396,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 +436,7 @@ 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);
 }
 
 /*
@@ -476,9 +452,9 @@ 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;
@@ -486,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -494,7 +470,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -545,14 +521,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -629,11 +605,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -668,10 +644,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -694,27 +670,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 +706,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 +718,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 +735,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 +767,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;
 	}
@@ -840,12 +816,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -853,12 +829,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -872,7 +848,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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 +864,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index ae3365d917..be7f175bf1 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);
 	TimestampTz result;
@@ -70,50 +80,80 @@ pg_stat_get_lastscan(PG_FUNCTION_ARGS)
 		PG_RETURN_TIMESTAMPTZ(result);
 }
 
-
 Datum
-pg_stat_get_tuples_returned(PG_FUNCTION_ARGS)
+pg_stat_get_index_lastscan(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
-	int64		result;
-	PgStat_StatTabEntry *tabentry;
+	TimestampTz result;
+	PgStat_StatIndEntry *indentry;
 
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)
 		result = 0;
 	else
-		result = (int64) (tabentry->tuples_returned);
+		result = indentry->lastscan;
+
+	if (result == 0)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_TIMESTAMPTZ(result);
+}
+
+Datum
+pg_stat_get_table_tuples_returned(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, 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);
+}
 
 Datum
 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);
 }
@@ -124,12 +164,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);
 }
@@ -140,12 +177,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);
 }
@@ -156,12 +190,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);
 }
@@ -172,12 +203,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);
 }
@@ -188,12 +216,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);
 }
@@ -204,12 +229,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);
 }
@@ -220,44 +242,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);
 }
@@ -339,12 +375,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);
 }
@@ -354,12 +387,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);
 }
@@ -369,12 +399,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);
 }
@@ -384,12 +411,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);
 }
@@ -1843,7 +1867,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;
@@ -1857,17 +1881,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);
 }
@@ -1877,12 +1916,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);
 }
@@ -1970,12 +2009,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);
 }
@@ -1985,12 +2024,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);
 }
@@ -2109,7 +2148,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..da9e60225f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2720,8 +2723,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 +6318,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 9dbe9ec801..8adbade072 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..149394c7ef 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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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 1d84407a03..e3f1ae7821 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;$$;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..12ec87a6d1 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
 ---
@@ -493,40 +564,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 f8302f1ed1..898f09206a 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


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-16 20:12           ` Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Andres Freund @ 2022-11-16 20:12 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 2022-11-15 10:48:40 +0100, Drouvot, Bertrand wrote:
> diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
> index ae3365d917..be7f175bf1 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);
>  }

This still leaves a fair bit of boilerplate. ISTM that the function body
really should just be a single line.

Might even be worth defining the whole function via a macro. Perhaps something like

PGSTAT_DEFINE_REL_FIELD_ACCESSOR(PGSTAT_KIND_INDEX, pg_stat_get_index, numscans);

I think the logic to infer which DB oid to use for a stats entry could be
shared between different kinds of stats. We don't need to duplicate it.

Is there any reason to not replace the "double lookup" in
pgstat_fetch_stat_tabentry() with IsSharedRelation()?


This should probably be done in a preparatory commit.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
@ 2022-11-18 11:18             ` Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-18 11:18 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/16/22 9:12 PM, Andres Freund wrote:
> Hi,
> 
> On 2022-11-15 10:48:40 +0100, Drouvot, Bertrand wrote:
>> diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
>> index ae3365d917..be7f175bf1 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);
>>   }
> 
> This still leaves a fair bit of boilerplate. ISTM that the function body
> really should just be a single line.
> 
> Might even be worth defining the whole function via a macro. Perhaps something like
> 
> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(PGSTAT_KIND_INDEX, pg_stat_get_index, numscans);

Thanks for the feedback!

Right, what about something like the following?

"
#define PGSTAT_FETCH_STAT_ENTRY(pgstat_entry_kind, pgstat_fetch_stat_function, relid, stat_name) \
	do { \
		pgstat_entry_kind *entry = pgstat_fetch_stat_function(relid); \
		PG_RETURN_INT64(entry == NULL ? 0 : (int64) (entry->stat_name)); \
	} while (0)

Datum
pg_stat_get_index_numscans(PG_FUNCTION_ARGS)
{
	PGSTAT_FETCH_STAT_ENTRY(PgStat_StatIndEntry, pgstat_fetch_stat_indentry, PG_GETARG_OID(0), numscans);
}
"

> 
> I think the logic to infer which DB oid to use for a stats entry could be
> shared between different kinds of stats. We don't need to duplicate it.
> 

Agree, will provide a new patch version once [1] is committed.


> Is there any reason to not replace the "double lookup" in
> pgstat_fetch_stat_tabentry() with IsSharedRelation()?
> 
> 

Thanks for the suggestion!

> This should probably be done in a preparatory commit.

Proposal submitted in [1].

[1]: https://www.postgresql.org/message-id/flat/2e4a0ae1-2696-9f0c-301c-2330e447133f%40gmail.com#e47bf5d2...

Regards,

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-20 23:19               ` Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Andres Freund @ 2022-11-20 23:19 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 2022-11-18 12:18:38 +0100, Drouvot, Bertrand wrote:
> On 11/16/22 9:12 PM, Andres Freund wrote:
> > This still leaves a fair bit of boilerplate. ISTM that the function body
> > really should just be a single line.
> > 
> > Might even be worth defining the whole function via a macro. Perhaps something like
> > 
> > PGSTAT_DEFINE_REL_FIELD_ACCESSOR(PGSTAT_KIND_INDEX, pg_stat_get_index, numscans);
> 
> Thanks for the feedback!
> 
> Right, what about something like the following?
> 
> "
> #define PGSTAT_FETCH_STAT_ENTRY(pgstat_entry_kind, pgstat_fetch_stat_function, relid, stat_name) \
> 	do { \
> 		pgstat_entry_kind *entry = pgstat_fetch_stat_function(relid); \
> 		PG_RETURN_INT64(entry == NULL ? 0 : (int64) (entry->stat_name)); \
> 	} while (0)
> 
> Datum
> pg_stat_get_index_numscans(PG_FUNCTION_ARGS)
> {
> 	PGSTAT_FETCH_STAT_ENTRY(PgStat_StatIndEntry, pgstat_fetch_stat_indentry, PG_GETARG_OID(0), numscans);
> }
> "

That's better, but still seems like quite a bit of repetition, given the
number of accessors. I think I like my idea of a macro defining the whole
function a bit better.

I'd define a "base" macro and then a version that's specific to tables and
indexes each, so that the pieces related to that don't have to be repeated as
often.


> > This should probably be done in a preparatory commit.
> 
> Proposal submitted in [1].

Now merged.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
@ 2022-11-21 13:32                 ` Drouvot, Bertrand <[email protected]>
  2022-11-22 06:19                   ` Re: Split index and table statistics into different types of stats Bharath Rupireddy <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-21 13:32 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/21/22 12:19 AM, Andres Freund wrote:
> Hi,
> 
> On 2022-11-18 12:18:38 +0100, Drouvot, Bertrand wrote:
>> On 11/16/22 9:12 PM, Andres Freund wrote:
>>> This still leaves a fair bit of boilerplate. ISTM that the function body
>>> really should just be a single line.
>>>
>>> Might even be worth defining the whole function via a macro. Perhaps something like
>>>
>>> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(PGSTAT_KIND_INDEX, pg_stat_get_index, numscans);
>>
>> Thanks for the feedback!
>>
>> Right, what about something like the following?
>>
>> "
>> #define PGSTAT_FETCH_STAT_ENTRY(pgstat_entry_kind, pgstat_fetch_stat_function, relid, stat_name) \
>> 	do { \
>> 		pgstat_entry_kind *entry = pgstat_fetch_stat_function(relid); \
>> 		PG_RETURN_INT64(entry == NULL ? 0 : (int64) (entry->stat_name)); \
>> 	} while (0)
>>
>> Datum
>> pg_stat_get_index_numscans(PG_FUNCTION_ARGS)
>> {
>> 	PGSTAT_FETCH_STAT_ENTRY(PgStat_StatIndEntry, pgstat_fetch_stat_indentry, PG_GETARG_OID(0), numscans);
>> }
>> "
> 
> That's better, but still seems like quite a bit of repetition, given the
> number of accessors. I think I like my idea of a macro defining the whole
> function a bit better.
> 

Got it, what about creating another preparatory commit to first introduce something like:

"
#define PGSTAT_DEFINE_REL_FIELD_ACCESSOR(function_name_prefix, stat_name) \
Datum \
function_name_prefix##_##stat_name(PG_FUNCTION_ARGS) \
{ \
Oid			relid = PG_GETARG_OID(0); \
int64		result; \
PgStat_StatTabEntry *tabentry; \
if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) \
	result = 0; \
else \
	result = (int64) (tabentry->stat_name); \
PG_RETURN_INT64(result); \
} \

PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, numscans);

PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, tuples_returned);
.
.
.
"

If that makes sense to you, I'll submit this preparatory patch.

> Now merged.

Thanks!

Regards,

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-11-22 06:19                   ` Bharath Rupireddy <[email protected]>
  2022-11-22 07:12                     ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Bharath Rupireddy @ 2022-11-22 06:19 UTC (permalink / raw)
  To: Drouvot, Bertrand <[email protected]>; +Cc: Andres Freund <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

On Mon, Nov 21, 2022 at 7:03 PM Drouvot, Bertrand
<[email protected]> wrote:
>
> On 11/21/22 12:19 AM, Andres Freund wrote:
> >
> > That's better, but still seems like quite a bit of repetition, given the
> > number of accessors. I think I like my idea of a macro defining the whole
> > function a bit better.
> >
>
> Got it, what about creating another preparatory commit to first introduce something like:
>
> "
> #define PGSTAT_DEFINE_REL_FIELD_ACCESSOR(function_name_prefix, stat_name) \
> Datum \
> function_name_prefix##_##stat_name(PG_FUNCTION_ARGS) \
> { \
> Oid                     relid = PG_GETARG_OID(0); \
> int64           result; \
> PgStat_StatTabEntry *tabentry; \
> if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) \
>         result = 0; \
> else \
>         result = (int64) (tabentry->stat_name); \
> PG_RETURN_INT64(result); \
> } \
>
> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, numscans);
>
> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, tuples_returned);
> .
> .
> .
> "
>
> If that makes sense to you, I'll submit this preparatory patch.

I think the macros stitching the function declarations and definitions
is a great idea to avoid code duplicacy. We seem to be using that
approach already - PG_FUNCTION_INFO_V1, SH_DECLARE, SH_DEFINE and its
friends, STEMMER_MODULE and so on. +1 for first applying this
principle for existing functions. Looking forward to the patch.

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-22 06:19                   ` Re: Split index and table statistics into different types of stats Bharath Rupireddy <[email protected]>
@ 2022-11-22 07:12                     ` Drouvot, Bertrand <[email protected]>
  2022-12-06 19:11                       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-11-22 07:12 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Andres Freund <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/22/22 7:19 AM, Bharath Rupireddy wrote:
> On Mon, Nov 21, 2022 at 7:03 PM Drouvot, Bertrand
> <[email protected]> wrote:
>>
>> On 11/21/22 12:19 AM, Andres Freund wrote:
>>>
>>> That's better, but still seems like quite a bit of repetition, given the
>>> number of accessors. I think I like my idea of a macro defining the whole
>>> function a bit better.
>>>
>>
>> Got it, what about creating another preparatory commit to first introduce something like:
>>
>> "
>> #define PGSTAT_DEFINE_REL_FIELD_ACCESSOR(function_name_prefix, stat_name) \
>> Datum \
>> function_name_prefix##_##stat_name(PG_FUNCTION_ARGS) \
>> { \
>> Oid                     relid = PG_GETARG_OID(0); \
>> int64           result; \
>> PgStat_StatTabEntry *tabentry; \
>> if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) \
>>          result = 0; \
>> else \
>>          result = (int64) (tabentry->stat_name); \
>> PG_RETURN_INT64(result); \
>> } \
>>
>> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, numscans);
>>
>> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, tuples_returned);
>> .
>> .
>> .
>> "
>>
>> If that makes sense to you, I'll submit this preparatory patch.
> 
> I think the macros stitching the function declarations and definitions
> is a great idea to avoid code duplicacy. We seem to be using that
> approach already - PG_FUNCTION_INFO_V1, SH_DECLARE, SH_DEFINE and its
> friends, STEMMER_MODULE and so on. +1 for first applying this
> principle for existing functions. Looking forward to the patch.
> 

Thanks! Patch proposal submitted in [1].

I'll resume working on the current thread once [1] is committed.

[1]: https://www.postgresql.org/message-id/d547a9bc-76c2-f875-df74-3ad6fd9d6236%40gmail.com

Regards,

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-22 06:19                   ` Re: Split index and table statistics into different types of stats Bharath Rupireddy <[email protected]>
  2022-11-22 07:12                     ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-12-06 19:11                       ` Drouvot, Bertrand <[email protected]>
  2022-12-07 10:11                         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-12-06 19:11 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Andres Freund <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 11/22/22 8:12 AM, Drouvot, Bertrand wrote:
> Hi,
> 
> On 11/22/22 7:19 AM, Bharath Rupireddy wrote:
>> On Mon, Nov 21, 2022 at 7:03 PM Drouvot, Bertrand
>> <[email protected]> wrote:
>>>
>>> On 11/21/22 12:19 AM, Andres Freund wrote:
>>>>
>>>> That's better, but still seems like quite a bit of repetition, given the
>>>> number of accessors. I think I like my idea of a macro defining the whole
>>>> function a bit better.
>>>>
>>>
>>> Got it, what about creating another preparatory commit to first introduce something like:
>>>
>>> "
>>> #define PGSTAT_DEFINE_REL_FIELD_ACCESSOR(function_name_prefix, stat_name) \
>>> Datum \
>>> function_name_prefix##_##stat_name(PG_FUNCTION_ARGS) \
>>> { \
>>> Oid                     relid = PG_GETARG_OID(0); \
>>> int64           result; \
>>> PgStat_StatTabEntry *tabentry; \
>>> if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL) \
>>>          result = 0; \
>>> else \
>>>          result = (int64) (tabentry->stat_name); \
>>> PG_RETURN_INT64(result); \
>>> } \
>>>
>>> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, numscans);
>>>
>>> PGSTAT_DEFINE_REL_FIELD_ACCESSOR(pg_stat_get, tuples_returned);
>>> .
>>> .
>>> .
>>> "
>>>
>>> If that makes sense to you, I'll submit this preparatory patch.
>>
>> I think the macros stitching the function declarations and definitions
>> is a great idea to avoid code duplicacy. We seem to be using that
>> approach already - PG_FUNCTION_INFO_V1, SH_DECLARE, SH_DEFINE and its
>> friends, STEMMER_MODULE and so on. +1 for first applying this
>> principle for existing functions. Looking forward to the patch.
>>
> 
> Thanks! Patch proposal submitted in [1].
> 
> I'll resume working on the current thread once [1] is committed.
> 
> [1]: https://www.postgresql.org/message-id/d547a9bc-76c2-f875-df74-3ad6fd9d6236%40gmail.com
> 

As [1] mentioned above has been committed (83a1a1b566), please find attached V5 related to this thread making use of the new macros (namely PG_STAT_GET_RELENTRY_INT64 and PG_STAT_GET_RELENTRY_TIMESTAMPTZ).

I switched from using "CppConcat" to using "##", as it looks to me it's easier to read now that we are adding another concatenation to the game (due to the table/index split).

The (Tab,tab) or (Ind,ind) passed as arguments to the macros look "weird" (I don't have a better idea yet): purpose is to follow the naming convention for PgStat_StatTabEntry/PgStat_StatIndEntry and pgstat_fetch_stat_tabentry/pgstat_fetch_stat_indentry, thoughts?

Looking forward to your feedback,

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..13b48b11e5
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index a9c05153d9..b9c29bd7c2 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 973979508d..dea762c116 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,124 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}																\
+#define PG_STAT_GET_RELENTRY_INT64(Relkind,relkind,stat)				\
+Datum																	\
+pg_stat_get_##relkind##_##stat(PG_FUNCTION_ARGS)						\
+{																		\
+	Oid			relid = PG_GETARG_OID(0);								\
+	int64		result;													\
+	PgStat_Stat##Relkind##Entry *entry;									\
+																		\
+	if ((entry = pgstat_fetch_stat_##relkind##entry(relid)) == NULL)	\
+		result = 0;														\
+	else																\
+		result = (int64) (entry->stat);									\
+																		\
+	PG_RETURN_INT64(result);											\
+}
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,analyze_count);
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,autoanalyze_count);
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,autovacuum_count);
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,blocks_fetched);
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,blocks_hit);
+
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,dead_tuples);
 
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count);
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,ins_since_vacuum);
 
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count);
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,live_tuples);
 
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count);
+/* pg_stat_get_mod_since_analyze */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,mod_since_analyze);
 
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched);
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,numscans);
 
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit);
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_deleted);
 
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples);
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_fetched);
 
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum);
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_hot_updated);
 
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples);
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_inserted);
 
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze);
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_returned);
 
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans);
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_updated);
 
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted);
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,vacuum_count);
 
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched);
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,numscans);
 
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated);
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,tuples_returned);
 
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted);
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,tuples_fetched);
 
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned);
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,blocks_fetched);
 
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated);
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,blocks_hit);
 
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count);
+#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Relkind,relkind,stat)			\
+Datum																	\
+pg_stat_get_##relkind##_##stat(PG_FUNCTION_ARGS)						\
+{																		\
+	Oid			relid = PG_GETARG_OID(0);								\
+	TimestampTz		result;												\
+	PgStat_Stat##Relkind##Entry *entry;									\
+																		\
+	if ((entry = pgstat_fetch_stat_##relkind##entry(relid)) == NULL)	\
+		result = 0;														\
+	else																\
+		result = entry->stat;											\
+																		\
+	if (result == 0)													\
+		PG_RETURN_NULL();												\
+	else																\
+		PG_RETURN_TIMESTAMPTZ(result);									\
+}
 
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
-}																\
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_analyze_time);
 
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time);
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_autoanalyze_time);
 
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time);
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_autovacuum_time);
 
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time);
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_vacuum_time);
 
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time);
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,lastscan);
 
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan);
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Ind,ind,lastscan);
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1586,7 +1604,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1600,17 +1618,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1620,12 +1653,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);
 }
@@ -1713,12 +1746,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);
 }
@@ -1728,12 +1761,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);
 }
@@ -1852,7 +1885,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 a50eecc7c8..e74704742c 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 f9301b2627..c3fe4af669 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 bc6349727b..57f5c8657a 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 532ea36990..c2b9f41bf2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 58daeca831..f9009263bc 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2008,10 +2008,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
@@ -2025,6 +2026,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


Attachments:

  [text/plain] v5-0001-split_tables_indexes_stats.patch (159.5K, ../../[email protected]/2-v5-0001-split_tables_indexes_stats.patch)
  download | inline diff:
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..13b48b11e5
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index a9c05153d9..b9c29bd7c2 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 973979508d..dea762c116 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,124 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}																\
+#define PG_STAT_GET_RELENTRY_INT64(Relkind,relkind,stat)				\
+Datum																	\
+pg_stat_get_##relkind##_##stat(PG_FUNCTION_ARGS)						\
+{																		\
+	Oid			relid = PG_GETARG_OID(0);								\
+	int64		result;													\
+	PgStat_Stat##Relkind##Entry *entry;									\
+																		\
+	if ((entry = pgstat_fetch_stat_##relkind##entry(relid)) == NULL)	\
+		result = 0;														\
+	else																\
+		result = (int64) (entry->stat);									\
+																		\
+	PG_RETURN_INT64(result);											\
+}
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,analyze_count);
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,autoanalyze_count);
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,autovacuum_count);
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,blocks_fetched);
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,blocks_hit);
+
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,dead_tuples);
 
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count);
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,ins_since_vacuum);
 
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count);
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,live_tuples);
 
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count);
+/* pg_stat_get_mod_since_analyze */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,mod_since_analyze);
 
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched);
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,numscans);
 
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit);
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_deleted);
 
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples);
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_fetched);
 
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum);
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_hot_updated);
 
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples);
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_inserted);
 
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze);
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_returned);
 
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans);
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,tuples_updated);
 
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted);
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_RELENTRY_INT64(Tab,tab,vacuum_count);
 
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched);
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,numscans);
 
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated);
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,tuples_returned);
 
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted);
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,tuples_fetched);
 
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned);
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,blocks_fetched);
 
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated);
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_RELENTRY_INT64(Ind,ind,blocks_hit);
 
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count);
+#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Relkind,relkind,stat)			\
+Datum																	\
+pg_stat_get_##relkind##_##stat(PG_FUNCTION_ARGS)						\
+{																		\
+	Oid			relid = PG_GETARG_OID(0);								\
+	TimestampTz		result;												\
+	PgStat_Stat##Relkind##Entry *entry;									\
+																		\
+	if ((entry = pgstat_fetch_stat_##relkind##entry(relid)) == NULL)	\
+		result = 0;														\
+	else																\
+		result = entry->stat;											\
+																		\
+	if (result == 0)													\
+		PG_RETURN_NULL();												\
+	else																\
+		PG_RETURN_TIMESTAMPTZ(result);									\
+}
 
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
-}																\
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_analyze_time);
 
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time);
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_autoanalyze_time);
 
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time);
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_autovacuum_time);
 
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time);
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,last_vacuum_time);
 
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time);
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Tab,tab,lastscan);
 
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan);
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_RELENTRY_TIMESTAMPTZ(Ind,ind,lastscan);
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1586,7 +1604,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1600,17 +1618,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1620,12 +1653,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);
 }
@@ -1713,12 +1746,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);
 }
@@ -1728,12 +1761,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);
 }
@@ -1852,7 +1885,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 a50eecc7c8..e74704742c 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 f9301b2627..c3fe4af669 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 bc6349727b..57f5c8657a 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 532ea36990..c2b9f41bf2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 58daeca831..f9009263bc 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2008,10 +2008,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
@@ -2025,6 +2026,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-22 06:19                   ` Re: Split index and table statistics into different types of stats Bharath Rupireddy <[email protected]>
  2022-11-22 07:12                     ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-12-06 19:11                       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-12-07 10:11                         ` Drouvot, Bertrand <[email protected]>
  2022-12-10 09:54                           ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  0 siblings, 1 reply; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-12-07 10:11 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Andres Freund <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

> Hi,
>  
> As [1] mentioned above has been committed (83a1a1b566), please find attached V5 related to this thread making use of the new macros (namely PG_STAT_GET_RELENTRY_INT64 and PG_STAT_GET_RELENTRY_TIMESTAMPTZ).
> 
> I switched from using "CppConcat" to using "##", as it looks to me it's easier to read now that we are adding another concatenation to the game (due to the table/index split).
> 
> The (Tab,tab) or (Ind,ind) passed as arguments to the macros look "weird" (I don't have a better idea yet): purpose is to follow the naming convention for PgStat_StatTabEntry/PgStat_StatIndEntry and pgstat_fetch_stat_tabentry/pgstat_fetch_stat_indentry, thoughts?
> 
> Looking forward to your feedback,
> 

Attaching V6 (mandatory rebase due to 8018ffbf58).

While at it, I got rid of the weirdness mentioned above by creating 2 sets of macros (one for the tables and one for the indexes).

Looking forward to your feedback,

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..820592c119
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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->tuples_returned += lstats->i_counts.i_tuples_returned;
+	dbentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+	dbentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+	dbentry->blocks_hit += lstats->i_counts.i_blocks_hit;
+
+	return true;
+}
+
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index f9788c30ae..7e4e0134df 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 25a159b5e5..31b3c1eb5d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,159 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}																\
-
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count);
-
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count);
-
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count);
-
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched);
-
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit);
-
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples);
-
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum);
-
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples);
-
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze);
-
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans);
-
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted);
-
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched);
-
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated);
-
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted);
-
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned);
-
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated);
-
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count);
-
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
-}																\
-
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time);
-
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time);
-
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time);
-
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time);
-
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan);
+#define PG_STAT_GET_TABENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	int64		result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = (int64) (tabentry->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}																	\
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_TABENTRY_INT64(analyze_count);
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_TABENTRY_INT64(autoanalyze_count);
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_TABENTRY_INT64(autovacuum_count);
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_TABENTRY_INT64(blocks_fetched);
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_TABENTRY_INT64(blocks_hit);
+
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_TABENTRY_INT64(dead_tuples);
+
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_TABENTRY_INT64(ins_since_vacuum);
+
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_TABENTRY_INT64(live_tuples);
+
+/* pg_stat_get_mod_since_analyze */
+PG_STAT_GET_TABENTRY_INT64(mod_since_analyze);
+
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_TABENTRY_INT64(numscans);
+
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_TABENTRY_INT64(tuples_deleted);
+
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_TABENTRY_INT64(tuples_fetched);
+
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_hot_updated);
+
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_TABENTRY_INT64(tuples_inserted);
+
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_TABENTRY_INT64(tuples_returned);
+
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_updated);
+
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_TABENTRY_INT64(vacuum_count);
+
+#define PG_STAT_GET_INDENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(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->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}																	\
+
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_INDENTRY_INT64(numscans);
+
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_INDENTRY_INT64(tuples_returned);
+
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_INDENTRY_INT64(tuples_fetched);
+
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_INDENTRY_INT64(blocks_fetched);
+
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_INDENTRY_INT64(blocks_hit);
+
+#define PG_STAT_GET_TABENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = tabentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}																	\
+
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_analyze_time);
+
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autoanalyze_time);
+
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autovacuum_time);
+
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_vacuum_time);
+
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(lastscan);
+
+#define PG_STAT_GET_INDENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatIndEntry *indentry;									\
+																	\
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = indentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}																	\
+
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_INDENTRY_TIMESTAMPTZ(lastscan);
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1308,7 +1361,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1322,17 +1375,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1342,12 +1410,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);
 }
@@ -1435,12 +1503,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);
 }
@@ -1450,12 +1518,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);
 }
@@ -1574,7 +1642,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 450e5124a5..f8e47f0f9d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 f9301b2627..c3fe4af669 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 a3df8d27c3..a35465bc92 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 fb9f936d43..9c2b813789 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 a2dfd5c9da..5784513538 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2008,10 +2008,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
@@ -2025,6 +2026,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


Attachments:

  [text/plain] v6-0001-split_tables_indexes_stats.patch (160.4K, ../../[email protected]/2-v6-0001-split_tables_indexes_stats.patch)
  download | inline diff:
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..820592c119
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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->tuples_returned += lstats->i_counts.i_tuples_returned;
+	dbentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+	dbentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+	dbentry->blocks_hit += lstats->i_counts.i_blocks_hit;
+
+	return true;
+}
+
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index f9788c30ae..7e4e0134df 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 25a159b5e5..31b3c1eb5d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,159 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}																\
-
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count);
-
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count);
-
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count);
-
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched);
-
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit);
-
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples);
-
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum);
-
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples);
-
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze);
-
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans);
-
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted);
-
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched);
-
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated);
-
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted);
-
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned);
-
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated);
-
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count);
-
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
-}																\
-
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time);
-
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time);
-
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time);
-
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time);
-
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan);
+#define PG_STAT_GET_TABENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	int64		result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = (int64) (tabentry->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}																	\
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_TABENTRY_INT64(analyze_count);
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_TABENTRY_INT64(autoanalyze_count);
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_TABENTRY_INT64(autovacuum_count);
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_TABENTRY_INT64(blocks_fetched);
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_TABENTRY_INT64(blocks_hit);
+
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_TABENTRY_INT64(dead_tuples);
+
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_TABENTRY_INT64(ins_since_vacuum);
+
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_TABENTRY_INT64(live_tuples);
+
+/* pg_stat_get_mod_since_analyze */
+PG_STAT_GET_TABENTRY_INT64(mod_since_analyze);
+
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_TABENTRY_INT64(numscans);
+
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_TABENTRY_INT64(tuples_deleted);
+
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_TABENTRY_INT64(tuples_fetched);
+
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_hot_updated);
+
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_TABENTRY_INT64(tuples_inserted);
+
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_TABENTRY_INT64(tuples_returned);
+
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_updated);
+
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_TABENTRY_INT64(vacuum_count);
+
+#define PG_STAT_GET_INDENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(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->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}																	\
+
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_INDENTRY_INT64(numscans);
+
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_INDENTRY_INT64(tuples_returned);
+
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_INDENTRY_INT64(tuples_fetched);
+
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_INDENTRY_INT64(blocks_fetched);
+
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_INDENTRY_INT64(blocks_hit);
+
+#define PG_STAT_GET_TABENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = tabentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}																	\
+
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_analyze_time);
+
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autoanalyze_time);
+
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autovacuum_time);
+
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_vacuum_time);
+
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(lastscan);
+
+#define PG_STAT_GET_INDENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatIndEntry *indentry;									\
+																	\
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = indentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}																	\
+
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_INDENTRY_TIMESTAMPTZ(lastscan);
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1308,7 +1361,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1322,17 +1375,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1342,12 +1410,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);
 }
@@ -1435,12 +1503,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);
 }
@@ -1450,12 +1518,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);
 }
@@ -1574,7 +1642,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 450e5124a5..f8e47f0f9d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 f9301b2627..c3fe4af669 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 a3df8d27c3..a35465bc92 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 fb9f936d43..9c2b813789 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 a2dfd5c9da..5784513538 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2008,10 +2008,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
@@ -2025,6 +2026,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
  2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-01 00:30 ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-02 08:58   ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-04 20:51     ` Re: Split index and table statistics into different types of stats Melanie Plageman <[email protected]>
  2022-11-14 10:36       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-15 09:48         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-16 20:12           ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-18 11:18             ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-20 23:19               ` Re: Split index and table statistics into different types of stats Andres Freund <[email protected]>
  2022-11-21 13:32                 ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-11-22 06:19                   ` Re: Split index and table statistics into different types of stats Bharath Rupireddy <[email protected]>
  2022-11-22 07:12                     ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-12-06 19:11                       ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
  2022-12-07 10:11                         ` Re: Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
@ 2022-12-10 09:54                           ` Drouvot, Bertrand <[email protected]>
  0 siblings, 0 replies; 17+ messages in thread

From: Drouvot, Bertrand @ 2022-12-10 09:54 UTC (permalink / raw)
  To: Bharath Rupireddy <[email protected]>; +Cc: Andres Freund <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On 12/7/22 11:11 AM, Drouvot, Bertrand wrote:
> Hi,
> 
>> Hi,
>>
>> As [1] mentioned above has been committed (83a1a1b566), please find attached V5 related to this thread making use of the new macros (namely PG_STAT_GET_RELENTRY_INT64 and PG_STAT_GET_RELENTRY_TIMESTAMPTZ).
>>
>> I switched from using "CppConcat" to using "##", as it looks to me it's easier to read now that we are adding another concatenation to the game (due to the table/index split).
>>
>> The (Tab,tab) or (Ind,ind) passed as arguments to the macros look "weird" (I don't have a better idea yet): purpose is to follow the naming convention for PgStat_StatTabEntry/PgStat_StatIndEntry and pgstat_fetch_stat_tabentry/pgstat_fetch_stat_indentry, thoughts?
>>
>> Looking forward to your feedback,
>>
> 
> Attaching V6 (mandatory rebase due to 8018ffbf58).
> 
> While at it, I got rid of the weirdness mentioned above by creating 2 sets of macros (one for the tables and one for the indexes).
> 
> Looking forward to your feedback,
> 
> Regards,
> 

Attaching V7, mandatory rebase due to 66dcb09246.

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..820592c119
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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->tuples_returned += lstats->i_counts.i_tuples_returned;
+	dbentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+	dbentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+	dbentry->blocks_hit += lstats->i_counts.i_blocks_hit;
+
+	return true;
+}
+
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index f9788c30ae..7e4e0134df 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 04a5a99002..729948378d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,159 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}
-
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count)
-
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count)
-
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count)
-
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched)
-
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit)
+#define PG_STAT_GET_TABENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	int64		result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = (int64) (tabentry->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_TABENTRY_INT64(analyze_count)
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_TABENTRY_INT64(autoanalyze_count)
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_TABENTRY_INT64(autovacuum_count)
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_TABENTRY_INT64(blocks_fetched)
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_TABENTRY_INT64(blocks_hit)
 
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples)
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_TABENTRY_INT64(dead_tuples)
 
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum)
-
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples)
-
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
-
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans)
-
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
-
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched)
-
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated)
-
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted)
-
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned)
-
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated)
-
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count)
-
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_TABENTRY_INT64(ins_since_vacuum)
+
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_TABENTRY_INT64(live_tuples)
+
+/* pg_stat_get_tab_mods_since_analyze */
+PG_STAT_GET_TABENTRY_INT64(mod_since_analyze)
+
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_TABENTRY_INT64(numscans)
+
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_TABENTRY_INT64(tuples_deleted)
+
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_TABENTRY_INT64(tuples_fetched)
+
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_hot_updated)
+
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_TABENTRY_INT64(tuples_inserted)
+
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_TABENTRY_INT64(tuples_returned)
+
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_updated)
+
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_TABENTRY_INT64(vacuum_count)
+
+#define PG_STAT_GET_INDENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(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->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}
+
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_INDENTRY_INT64(numscans)
+
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_INDENTRY_INT64(tuples_returned)
+
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_INDENTRY_INT64(tuples_fetched)
+
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_INDENTRY_INT64(blocks_fetched)
+
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_INDENTRY_INT64(blocks_hit)
+
+#define PG_STAT_GET_TABENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = tabentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
 }
 
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time)
-
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time)
-
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time)
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_analyze_time)
 
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
-
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autoanalyze_time)
+
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autovacuum_time)
+
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_vacuum_time)
+
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(lastscan)
+
+#define PG_STAT_GET_INDENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatIndEntry *indentry;									\
+																	\
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = indentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}
+
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_INDENTRY_TIMESTAMPTZ(lastscan)
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1308,7 +1361,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1322,17 +1375,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1342,12 +1410,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);
 }
@@ -1435,12 +1503,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);
 }
@@ -1450,12 +1518,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);
 }
@@ -1574,7 +1642,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 450e5124a5..f8e47f0f9d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 719599649a..9b83e14c57 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 a3df8d27c3..a35465bc92 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 fb9f936d43..9c2b813789 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 60c71d05fe..3be9c0e1ff 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
@@ -2026,6 +2027,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


Attachments:

  [text/plain] v7-0001-split_tables_indexes_stats.patch (160.2K, ../../[email protected]/2-v7-0001-split_tables_indexes_stats.patch)
  download | inline diff:
diff --git a/src/backend/access/common/relation.c b/src/backend/access/common/relation.c
index 382a42ff7d..259bd06aed 100644
--- a/src/backend/access/common/relation.c
+++ b/src/backend/access/common/relation.c
@@ -73,7 +73,10 @@ relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
@@ -123,7 +126,10 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
 	if (RelationUsesLocalBuffers(r))
 		MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
 
-	pgstat_init_relation(r);
+	if (r->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_init_index(r);
+	else
+		pgstat_init_table(r);
 
 	return r;
 }
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index dc303995e5..ceebc79302 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -590,7 +590,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 bdd413f01b..d5b71fdd1e 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..a612ae81ed 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -655,29 +655,29 @@ 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_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,
-            pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
-            pg_stat_get_live_tuples(C.oid) AS n_live_tup,
-            pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
-            pg_stat_get_mod_since_analyze(C.oid) AS n_mod_since_analyze,
-            pg_stat_get_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
-            pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
-            pg_stat_get_last_autovacuum_time(C.oid) as last_autovacuum,
-            pg_stat_get_last_analyze_time(C.oid) as last_analyze,
-            pg_stat_get_last_autoanalyze_time(C.oid) as last_autoanalyze,
-            pg_stat_get_vacuum_count(C.oid) AS vacuum_count,
-            pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,
-            pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_tab_numscans(C.oid) AS seq_scan,
+            pg_stat_get_tab_lastscan(C.oid) AS last_seq_scan,
+            pg_stat_get_tab_tuples_returned(C.oid) AS seq_tup_read,
+            sum(pg_stat_get_ind_numscans(I.indexrelid))::bigint AS idx_scan,
+            max(pg_stat_get_ind_lastscan(I.indexrelid)) AS last_idx_scan,
+            sum(pg_stat_get_ind_tuples_fetched(I.indexrelid))::bigint +
+            pg_stat_get_tab_tuples_fetched(C.oid) AS idx_tup_fetch,
+            pg_stat_get_tab_tuples_inserted(C.oid) AS n_tup_ins,
+            pg_stat_get_tab_tuples_updated(C.oid) AS n_tup_upd,
+            pg_stat_get_tab_tuples_deleted(C.oid) AS n_tup_del,
+            pg_stat_get_tab_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
+            pg_stat_get_tab_live_tuples(C.oid) AS n_live_tup,
+            pg_stat_get_tab_dead_tuples(C.oid) AS n_dead_tup,
+            pg_stat_get_tab_mod_since_analyze(C.oid) AS n_mod_since_analyze,
+            pg_stat_get_tab_ins_since_vacuum(C.oid) AS n_ins_since_vacuum,
+            pg_stat_get_tab_last_vacuum_time(C.oid) as last_vacuum,
+            pg_stat_get_tab_last_autovacuum_time(C.oid) as last_autovacuum,
+            pg_stat_get_tab_last_analyze_time(C.oid) as last_analyze,
+            pg_stat_get_tab_last_autoanalyze_time(C.oid) as last_autoanalyze,
+            pg_stat_get_tab_vacuum_count(C.oid) AS vacuum_count,
+            pg_stat_get_tab_autovacuum_count(C.oid) AS autovacuum_count,
+            pg_stat_get_tab_analyze_count(C.oid) AS analyze_count,
+            pg_stat_get_tab_autoanalyze_count(C.oid) AS autoanalyze_count
     FROM pg_class C LEFT JOIN
          pg_index I ON C.oid = I.indrelid
          LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
@@ -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_tab_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_ind_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_tab_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS heap_blks_read,
+            pg_stat_get_tab_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_tab_blocks_fetched(T.oid) -
+                    pg_stat_get_tab_blocks_hit(T.oid) AS toast_blks_read,
+            pg_stat_get_tab_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_blocks_fetched(indexrelid) -
+                         pg_stat_get_ind_blocks_hit(indexrelid))::bigint
                      AS idx_blks_read,
-                     sum(pg_stat_get_blocks_hit(indexrelid))::bigint
+                     sum(pg_stat_get_ind_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_ind_numscans(I.oid) AS idx_scan,
+            pg_stat_get_ind_lastscan(I.oid) AS last_idx_scan,
+            pg_stat_get_ind_tuples_returned(I.oid) AS idx_tup_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(I.oid) -
+                    pg_stat_get_ind_blocks_hit(I.oid) AS idx_blks_read,
+            pg_stat_get_ind_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_ind_blocks_fetched(C.oid) -
+                    pg_stat_get_tab_blocks_hit(C.oid) AS blks_read,
+            pg_stat_get_tab_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/Makefile b/src/backend/utils/activity/Makefile
index a2e8507fd6..7441f4ce85 100644
--- a/src/backend/utils/activity/Makefile
+++ b/src/backend/utils/activity/Makefile
@@ -22,11 +22,12 @@ OBJS = \
 	pgstat_checkpointer.o \
 	pgstat_database.o \
 	pgstat_function.o \
-	pgstat_relation.o \
+	pgstat_index.o \
 	pgstat_replslot.o \
 	pgstat_shmem.o \
 	pgstat_slru.o \
 	pgstat_subscription.o \
+	pgstat_table.o \
 	pgstat_wal.o \
 	pgstat_xact.o \
 	wait_event.o
diff --git a/src/backend/utils/activity/meson.build b/src/backend/utils/activity/meson.build
index 5b3b558a67..e6bf3c59b6 100644
--- a/src/backend/utils/activity/meson.build
+++ b/src/backend/utils/activity/meson.build
@@ -7,11 +7,12 @@ backend_sources += files(
   'pgstat_checkpointer.c',
   'pgstat_database.c',
   'pgstat_function.c',
-  'pgstat_relation.c',
+  'pgstat_index.c',
   'pgstat_replslot.c',
   'pgstat_shmem.c',
   'pgstat_slru.c',
   'pgstat_subscription.c',
+  'pgstat_table.c',
   'pgstat_wal.c',
   'pgstat_xact.c',
   'wait_event.c',
diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 1ebe3bbf29..d49a5ea4bb 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -72,10 +72,11 @@
  * - pgstat_checkpointer.c
  * - pgstat_database.c
  * - pgstat_function.c
- * - pgstat_relation.c
+ * - pgstat_index.c
  * - pgstat_replslot.c
  * - pgstat_slru.c
  * - pgstat_subscription.c
+ * - pgstat_table.c
  * - pgstat_wal.c
  *
  * Whenever possible infrastructure files should not contain code related to
@@ -269,18 +270,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_index.c b/src/backend/utils/activity/pgstat_index.c
new file mode 100644
index 0000000000..820592c119
--- /dev/null
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -0,0 +1,297 @@
+/* -------------------------------------------------------------------------
+ *
+ * pgstat_index.c
+ *	  Implementation of index statistics.
+ *
+ * This file contains the implementation of function index. It is kept
+ * separate from pgstat.c to enforce the line between the statistics access /
+ * storage implementation and the details about individual types of
+ * statistics.
+ *
+ * Copyright (c) 2001-2022, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/activity/pgstat_index.c
+ * -------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "utils/pgstat_internal.h"
+#include "utils/rel.h"
+#include "catalog/catalog.h"
+
+static PgStat_IndexStatus *pgstat_prep_index_pending(Oid rel_id, bool isshared);
+
+/*
+ * Copy stats between indexes. This is used for things like REINDEX
+ * CONCURRENTLY.
+ */
+void
+pgstat_copy_index_stats(Relation dst, Relation src)
+{
+	PgStat_StatIndEntry *srcstats;
+	PgStatShared_Index *dstshstats;
+	PgStat_EntryRef *dst_ref;
+
+	srcstats = pgstat_fetch_stat_indentry_ext(src->rd_rel->relisshared,
+											  RelationGetRelid(src));
+	if (!srcstats)
+		return;
+
+	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INDEX,
+										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+										  RelationGetRelid(dst),
+										  false);
+
+	dstshstats = (PgStatShared_Index *) dst_ref->shared_stats;
+	dstshstats->stats = *srcstats;
+
+	pgstat_unlock_entry(dst_ref);
+}
+
+/*
+ * Initialize a relcache entry to count access statistics.  Called whenever an
+ * index is opened.
+ *
+ * We assume that a relcache entry's pgstatind_info field is 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
+ * pgstat_assoc_index().
+ */
+void
+pgstat_init_index(Relation rel)
+{
+	/*
+	 * We only count stats for indexes
+	 */
+	Assert(rel->rd_rel->relkind == RELKIND_INDEX);
+
+	if (!pgstat_track_counts)
+	{
+		if (rel->pgstatind_info != NULL)
+			pgstat_unlink_index(rel);
+
+		/* We're not counting at all */
+		rel->pgstat_enabled = false;
+		rel->pgstatind_info = NULL;
+		return;
+	}
+
+	rel->pgstat_enabled = true;
+}
+
+/*
+ * 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_index() 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->pgstatind_info->relation == NULL);
+
+	/* mark this relation as the owner */
+	rel->pgstatind_info->relation = rel;
+}
+
+/*
+ * Break the mutual link between a relcache entry and pending index stats entry.
+ * This must be called whenever one end of the link is removed.
+ */
+void
+pgstat_unlink_index(Relation rel)
+{
+
+	if (rel->pgstatind_info == NULL)
+		return;
+
+	/* 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 index stats are dropped if transaction aborts.
+ */
+void
+pgstat_create_index(Relation rel)
+{
+	pgstat_create_transactional(PGSTAT_KIND_INDEX,
+								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+								RelationGetRelid(rel));
+}
+
+/*
+ * Ensure that index stats are dropped if transaction commits.
+ */
+void
+pgstat_drop_index(Relation rel)
+{
+	pgstat_drop_transactional(PGSTAT_KIND_INDEX,
+							  rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
+							  RelationGetRelid(rel));
+}
+
+/*
+ * 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)
+{
+	return pgstat_fetch_stat_indentry_ext(IsSharedRelation(relid), relid);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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;
+	return NULL;
+}
+
+/*
+ * 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->tuples_returned += lstats->i_counts.i_tuples_returned;
+	dbentry->tuples_fetched += lstats->i_counts.i_tuples_fetched;
+	dbentry->blocks_fetched += lstats->i_counts.i_blocks_fetched;
+	dbentry->blocks_hit += lstats->i_counts.i_blocks_hit;
+
+	return true;
+}
+
+void
+pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
+{
+	PgStat_IndexStatus *pending = (PgStat_IndexStatus *) entry_ref->pending;
+
+	if (pending->relation)
+		pgstat_unlink_index(pending->relation);
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/utils/activity/pgstat_relation.c b/src/backend/utils/activity/pgstat_table.c
similarity index 75%
rename from src/backend/utils/activity/pgstat_relation.c
rename to src/backend/utils/activity/pgstat_table.c
index f9788c30ae..7e4e0134df 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_table.c
@@ -1,9 +1,9 @@
 /* -------------------------------------------------------------------------
  *
- * pgstat_relation.c
- *	  Implementation of relation statistics.
+ * pgstat_table.c
+ *	  Implementation of table statistics.
  *
- * This file contains the implementation of function relation. It is kept
+ * This file contains the implementation of function table. It is kept
  * separate from pgstat.c to enforce the line between the statistics access /
  * storage implementation and the details about individual types of
  * statistics.
@@ -11,7 +11,7 @@
  * Copyright (c) 2001-2022, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  src/backend/utils/activity/pgstat_relation.c
+ *	  src/backend/utils/activity/pgstat_table.c
  * -------------------------------------------------------------------------
  */
 
@@ -44,74 +44,48 @@ 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 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
- * CONCURRENTLY.
- */
-void
-pgstat_copy_relation_stats(Relation dst, Relation src)
-{
-	PgStat_StatTabEntry *srcstats;
-	PgStatShared_Relation *dstshstats;
-	PgStat_EntryRef *dst_ref;
-
-	srcstats = pgstat_fetch_stat_tabentry_ext(src->rd_rel->relisshared,
-											  RelationGetRelid(src));
-	if (!srcstats)
-		return;
-
-	dst_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
-										  dst->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
-										  RelationGetRelid(dst),
-										  false);
-
-	dstshstats = (PgStatShared_Relation *) dst_ref->shared_stats;
-	dstshstats->stats = *srcstats;
-
-	pgstat_unlock_entry(dst_ref);
-}
-
 /*
  * Initialize a relcache entry to count access statistics.  Called whenever a
- * relation is opened.
+ * table is opened.
  *
- * We assume that a relcache entry's pgstat_info field is zeroed by relcache.c
+ * We assume that a relcache entry's pgstattab_info field is 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
- * pgstat_assoc_relation().
+ * pgstat_assoc_table().
  */
 void
-pgstat_init_relation(Relation rel)
+pgstat_init_table(Relation rel)
 {
 	char		relkind = rel->rd_rel->relkind;
 
+	Assert(relkind != RELKIND_INDEX);
+
 	/*
 	 * We only count stats for relations with storage and partitioned tables
 	 */
 	if (!RELKIND_HAS_STORAGE(relkind) && relkind != RELKIND_PARTITIONED_TABLE)
 	{
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
 	if (!pgstat_track_counts)
 	{
-		if (rel->pgstat_info)
-			pgstat_unlink_relation(rel);
+		if (rel->pgstattab_info != NULL)
+			pgstat_unlink_table(rel);
 
 		/* We're not counting at all */
 		rel->pgstat_enabled = false;
-		rel->pgstat_info = NULL;
+		rel->pgstattab_info = NULL;
 		return;
 	}
 
@@ -119,89 +93,92 @@ 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.
  *
- * This is separate from pgstat_init_relation() as it is not uncommon for
+ * This is separate from pgstat_init_table() as it is not uncommon for
  * 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->pgstat_info->relation == NULL);
+	Assert(rel->pgstattab_info->relation == NULL);
 
 	/* mark this relation as the owner */
-	rel->pgstat_info->relation = rel;
+	rel->pgstattab_info->relation = rel;
 }
 
 /*
- * Break the mutual link between a relcache entry and pending stats entry.
+ * Break the mutual link between a relcache entry and pending table stats entry.
  * This must be called whenever one end of the link is removed.
  */
 void
-pgstat_unlink_relation(Relation rel)
+pgstat_unlink_table(Relation rel)
 {
-	/* remove the link to stats info if any */
-	if (rel->pgstat_info == NULL)
+
+	if (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;
+	}
 }
 
 /*
- * Ensure that stats are dropped if transaction aborts.
+ * Ensure that table stats are dropped if transaction aborts.
  */
 void
-pgstat_create_relation(Relation rel)
+pgstat_create_table(Relation rel)
 {
-	pgstat_create_transactional(PGSTAT_KIND_RELATION,
+	pgstat_create_transactional(PGSTAT_KIND_TABLE,
 								rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId,
 								RelationGetRelid(rel));
 }
 
 /*
- * Ensure that stats are dropped if transaction commits.
+ * Ensure that table stats are dropped if transaction commits.
  */
 void
-pgstat_drop_relation(Relation rel)
+pgstat_drop_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;
 	}
 }
 
@@ -213,7 +190,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;
@@ -225,10 +202,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->live_tuples = livetuples;
@@ -272,7 +249,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);
 
@@ -291,31 +268,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->live_tuples = livetuples;
@@ -349,12 +326,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;
 	}
 }
 
@@ -364,16 +341,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++;
 	}
 }
 
@@ -383,12 +360,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++;
 	}
 }
 
@@ -398,15 +375,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;
 	}
 }
 
@@ -421,11 +398,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;
 	}
 }
 
@@ -451,7 +428,7 @@ 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);
 }
 
 /*
@@ -467,9 +444,9 @@ 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;
@@ -477,7 +454,7 @@ find_tabstat_entry(Oid rel_id)
 }
 
 /*
- * Perform relation stats specific end-of-transaction work. Helper for
+ * Perform table stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
  * Transfer transactional insert/update counts into the base tabstat entries.
@@ -485,7 +462,7 @@ find_tabstat_entry(Oid rel_id)
  * TopTransactionContext and will go away anyway.
  */
 void
-AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
+AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -536,14 +513,14 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit)
 }
 
 /*
- * Perform relation stats specific end-of-sub-transaction work. Helper for
+ * Perform table stats specific end-of-sub-transaction work. Helper for
  * AtEOSubXact_PgStat.
  *
  * Transfer transactional insert/update counts into the next higher
  * subtransaction state.
  */
 void
-AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
+AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth)
 {
 	PgStat_TableXactStatus *trans;
 	PgStat_TableXactStatus *next_trans;
@@ -620,11 +597,11 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, in
 }
 
 /*
- * Generate 2PC records for all the pending transaction-dependent relation
+ * Generate 2PC records for all the pending transaction-dependent table
  * stats.
  */
 void
-AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -659,10 +636,10 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
  * reported to the stats system immediately, while the effects on live and
  * dead tuple counts are preserved in the 2PC state file.
  *
- * Note: AtEOXact_PgStat_Relations is not called during PREPARE.
+ * Note: AtEOXact_PgStat_Tables is not called during PREPARE.
  */
 void
-PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
+PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state)
 {
 	PgStat_TableXactStatus *trans;
 
@@ -685,27 +662,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;
 }
@@ -721,10 +698,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)
@@ -733,10 +710,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;
 }
 
@@ -750,22 +727,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)
@@ -783,6 +759,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;
 	}
@@ -831,12 +808,12 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 }
 
 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;
 
 	if (pending->relation)
-		pgstat_unlink_relation(pending->relation);
+		pgstat_unlink_table(pending->relation);
 }
 
 /*
@@ -844,12 +821,12 @@ pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
  * 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;
@@ -863,7 +840,7 @@ pgstat_prep_relation_pending(Oid rel_id, bool isshared)
  * 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;
@@ -879,24 +856,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/activity/pgstat_xact.c b/src/backend/utils/activity/pgstat_xact.c
index 5a3aca4aef..870f1b1103 100644
--- a/src/backend/utils/activity/pgstat_xact.c
+++ b/src/backend/utils/activity/pgstat_xact.c
@@ -51,7 +51,7 @@ AtEOXact_PgStat(bool isCommit, bool parallel)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtEOXact_PgStat_Relations(xact_state, isCommit);
+		AtEOXact_PgStat_Tables(xact_state, isCommit);
 		AtEOXact_PgStat_DroppedStats(xact_state, isCommit);
 	}
 	pgStatXactStack = NULL;
@@ -122,7 +122,7 @@ AtEOSubXact_PgStat(bool isCommit, int nestDepth)
 		/* delink xact_state from stack immediately to simplify reuse case */
 		pgStatXactStack = xact_state->prev;
 
-		AtEOSubXact_PgStat_Relations(xact_state, isCommit, nestDepth);
+		AtEOSubXact_PgStat_Tables(xact_state, isCommit, nestDepth);
 		AtEOSubXact_PgStat_DroppedStats(xact_state, isCommit, nestDepth);
 
 		pfree(xact_state);
@@ -197,7 +197,7 @@ AtPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		AtPrepare_PgStat_Relations(xact_state);
+		AtPrepare_PgStat_Tables(xact_state);
 	}
 }
 
@@ -221,7 +221,7 @@ PostPrepare_PgStat(void)
 		Assert(xact_state->nest_level == 1);
 		Assert(xact_state->prev == NULL);
 
-		PostPrepare_PgStat_Relations(xact_state);
+		PostPrepare_PgStat_Tables(xact_state);
 	}
 	pgStatXactStack = NULL;
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 04a5a99002..729948378d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -36,106 +36,159 @@
 
 #define HAS_PGSTAT_PERMISSIONS(role)	 (has_privs_of_role(GetUserId(), ROLE_PG_READ_ALL_STATS) || has_privs_of_role(GetUserId(), role))
 
-#define PG_STAT_GET_RELENTRY_INT64(stat)						\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	int64		result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = (int64) (tabentry->stat);						\
-																\
-	PG_RETURN_INT64(result);									\
-}
-
-/* pg_stat_get_analyze_count */
-PG_STAT_GET_RELENTRY_INT64(analyze_count)
-
-/* pg_stat_get_autoanalyze_count */
-PG_STAT_GET_RELENTRY_INT64(autoanalyze_count)
-
-/* pg_stat_get_autovacuum_count */
-PG_STAT_GET_RELENTRY_INT64(autovacuum_count)
-
-/* pg_stat_get_blocks_fetched */
-PG_STAT_GET_RELENTRY_INT64(blocks_fetched)
-
-/* pg_stat_get_blocks_hit */
-PG_STAT_GET_RELENTRY_INT64(blocks_hit)
+#define PG_STAT_GET_TABENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	int64		result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = (int64) (tabentry->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}
+
+/* pg_stat_get_tab_analyze_count */
+PG_STAT_GET_TABENTRY_INT64(analyze_count)
+
+/* pg_stat_get_tab_autoanalyze_count */
+PG_STAT_GET_TABENTRY_INT64(autoanalyze_count)
+
+/* pg_stat_get_tab_autovacuum_count */
+PG_STAT_GET_TABENTRY_INT64(autovacuum_count)
+
+/* pg_stat_get_tab_blocks_fetched */
+PG_STAT_GET_TABENTRY_INT64(blocks_fetched)
+
+/* pg_stat_get_tab_blocks_hit */
+PG_STAT_GET_TABENTRY_INT64(blocks_hit)
 
-/* pg_stat_get_dead_tuples */
-PG_STAT_GET_RELENTRY_INT64(dead_tuples)
+/* pg_stat_get_tab_dead_tuples */
+PG_STAT_GET_TABENTRY_INT64(dead_tuples)
 
-/* pg_stat_get_ins_since_vacuum */
-PG_STAT_GET_RELENTRY_INT64(ins_since_vacuum)
-
-/* pg_stat_get_live_tuples */
-PG_STAT_GET_RELENTRY_INT64(live_tuples)
-
-/* pg_stat_get_mods_since_analyze */
-PG_STAT_GET_RELENTRY_INT64(mod_since_analyze)
-
-/* pg_stat_get_numscans */
-PG_STAT_GET_RELENTRY_INT64(numscans)
-
-/* pg_stat_get_tuples_deleted */
-PG_STAT_GET_RELENTRY_INT64(tuples_deleted)
-
-/* pg_stat_get_tuples_fetched */
-PG_STAT_GET_RELENTRY_INT64(tuples_fetched)
-
-/* pg_stat_get_tuples_hot_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_hot_updated)
-
-/* pg_stat_get_tuples_inserted */
-PG_STAT_GET_RELENTRY_INT64(tuples_inserted)
-
-/* pg_stat_get_tuples_returned */
-PG_STAT_GET_RELENTRY_INT64(tuples_returned)
-
-/* pg_stat_get_tuples_updated */
-PG_STAT_GET_RELENTRY_INT64(tuples_updated)
-
-/* pg_stat_get_vacuum_count */
-PG_STAT_GET_RELENTRY_INT64(vacuum_count)
-
-#define PG_STAT_GET_RELENTRY_TIMESTAMPTZ(stat)					\
-Datum															\
-CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)					\
-{																\
-	Oid			relid = PG_GETARG_OID(0);						\
-	TimestampTz result;											\
-	PgStat_StatTabEntry *tabentry;								\
-																\
-	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)	\
-		result = 0;												\
-	else														\
-		result = tabentry->stat;								\
-																\
-	if (result == 0)											\
-		PG_RETURN_NULL();										\
-	else														\
-		PG_RETURN_TIMESTAMPTZ(result);							\
+/* pg_stat_get_tab_ins_since_vacuum */
+PG_STAT_GET_TABENTRY_INT64(ins_since_vacuum)
+
+/* pg_stat_get_tab_live_tuples */
+PG_STAT_GET_TABENTRY_INT64(live_tuples)
+
+/* pg_stat_get_tab_mods_since_analyze */
+PG_STAT_GET_TABENTRY_INT64(mod_since_analyze)
+
+/* pg_stat_get_tab_numscans */
+PG_STAT_GET_TABENTRY_INT64(numscans)
+
+/* pg_stat_get_tab_tuples_deleted */
+PG_STAT_GET_TABENTRY_INT64(tuples_deleted)
+
+/* pg_stat_get_tab_tuples_fetched */
+PG_STAT_GET_TABENTRY_INT64(tuples_fetched)
+
+/* pg_stat_get_tab_tuples_hot_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_hot_updated)
+
+/* pg_stat_get_tab_tuples_inserted */
+PG_STAT_GET_TABENTRY_INT64(tuples_inserted)
+
+/* pg_stat_get_tab_tuples_returned */
+PG_STAT_GET_TABENTRY_INT64(tuples_returned)
+
+/* pg_stat_get_tab_tuples_updated */
+PG_STAT_GET_TABENTRY_INT64(tuples_updated)
+
+/* pg_stat_get_tab_vacuum_count */
+PG_STAT_GET_TABENTRY_INT64(vacuum_count)
+
+#define PG_STAT_GET_INDENTRY_INT64(stat)							\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(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->stat);							\
+																	\
+	PG_RETURN_INT64(result);										\
+}
+
+/* pg_stat_get_ind_numscans */
+PG_STAT_GET_INDENTRY_INT64(numscans)
+
+/* pg_stat_get_ind_tuples_returned */
+PG_STAT_GET_INDENTRY_INT64(tuples_returned)
+
+/* pg_stat_get_ind_tuples_fetched */
+PG_STAT_GET_INDENTRY_INT64(tuples_fetched)
+
+/* pg_stat_get_ind_blocks_fetched */
+PG_STAT_GET_INDENTRY_INT64(blocks_fetched)
+
+/* pg_stat_get_ind_blocks_hit */
+PG_STAT_GET_INDENTRY_INT64(blocks_hit)
+
+#define PG_STAT_GET_TABENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_tab_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatTabEntry *tabentry;									\
+																	\
+	if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = tabentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
 }
 
-/* pg_stat_get_last_analyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_analyze_time)
-
-/* pg_stat_get_last_autoanalyze_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autoanalyze_time)
-
-/* pg_stat_get_last_autovacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_autovacuum_time)
+/* pg_stat_get_tab_last_analyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_analyze_time)
 
-/* pg_stat_get_last_vacuum_time */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
-
-/* pg_stat_get_lastscan */
-PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)
+/* pg_stat_get_tab_last_autoanalyze_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autoanalyze_time)
+
+/* pg_stat_get_tab_last_autovacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_autovacuum_time)
+
+/* pg_stat_get_tab_last_vacuum_time */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(last_vacuum_time)
+
+/* pg_stat_get_tab_lastscan */
+PG_STAT_GET_TABENTRY_TIMESTAMPTZ(lastscan)
+
+#define PG_STAT_GET_INDENTRY_TIMESTAMPTZ(stat)						\
+Datum																\
+CppConcat(pg_stat_get_ind_,stat)(PG_FUNCTION_ARGS)					\
+{																	\
+	Oid			relid = PG_GETARG_OID(0);							\
+	TimestampTz result;												\
+	PgStat_StatIndEntry *indentry;									\
+																	\
+	if ((indentry = pgstat_fetch_stat_indentry(relid)) == NULL)		\
+		result = 0;													\
+	else															\
+		result = indentry->stat;									\
+																	\
+	if (result == 0)												\
+		PG_RETURN_NULL();											\
+	else															\
+		PG_RETURN_TIMESTAMPTZ(result);								\
+}
+
+/* pg_stat_get_ind_lastscan */
+PG_STAT_GET_INDENTRY_TIMESTAMPTZ(lastscan)
 
 Datum
 pg_stat_get_function_calls(PG_FUNCTION_ARGS)
@@ -1308,7 +1361,7 @@ pg_stat_get_slru(PG_FUNCTION_ARGS)
 }
 
 Datum
-pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
+pg_stat_get_tab_xact_numscans(PG_FUNCTION_ARGS)
 {
 	Oid			relid = PG_GETARG_OID(0);
 	int64		result;
@@ -1322,17 +1375,32 @@ pg_stat_get_xact_numscans(PG_FUNCTION_ARGS)
 	PG_RETURN_INT64(result);
 }
 
+Datum
+pg_stat_get_ind_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);
 }
@@ -1342,12 +1410,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);
 }
@@ -1435,12 +1503,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);
 }
@@ -1450,12 +1518,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);
 }
@@ -1574,7 +1642,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 450e5124a5..f8e47f0f9d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2411,7 +2411,10 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
 	RelationCloseSmgr(relation);
 
 	/* break mutual link with stats entry */
-	pgstat_unlink_relation(relation);
+	if (relation->rd_rel->relkind == RELKIND_INDEX)
+		pgstat_unlink_index(relation);
+	else
+		pgstat_unlink_table(relation);
 
 	/*
 	 * Free all the subsidiary data structures of the relcache entry, then the
@@ -2727,8 +2730,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)
@@ -6321,7 +6325,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 719599649a..9b83e14c57 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5274,96 +5274,120 @@
   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_tab_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_tab_numscans' },
+{ oid => '8296', descr => 'statistics: number of scans done for index',
+  proname => 'pg_stat_get_ind_numscans', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_numscans' },
+{ oid => '9976', descr => 'statistics: time of the last scan for table',
+  proname => 'pg_stat_get_tab_lastscan', provolatile => 's', proparallel => 'r',
+  prorettype => 'timestamptz', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_lastscan' },
+{ oid => '8626', descr => 'statistics: time of the last scan for index',
+  proname => 'pg_stat_get_ind_lastscan', provolatile => 's', proparallel => 'r',
   prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_lastscan' },
+  prosrc => 'pg_stat_get_ind_lastscan' },
 { oid => '1929', descr => 'statistics: number of tuples read by seqscan',
-  proname => 'pg_stat_get_tuples_returned', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_returned', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_returned' },
+  prosrc => 'pg_stat_get_tab_tuples_returned' },
+{ oid => '9603', descr => 'statistics: number of tuples read by seqscan',
+  proname => 'pg_stat_get_ind_tuples_returned', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_tuples_returned' },
 { oid => '1930', descr => 'statistics: number of tuples fetched by idxscan',
-  proname => 'pg_stat_get_tuples_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_tuples_fetched' },
+{ oid => '8526', descr => 'statistics: number of tuples fetched by idxscan',
+  proname => 'pg_stat_get_ind_tuples_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_fetched' },
+  prosrc => 'pg_stat_get_ind_tuples_fetched' },
 { oid => '1931', descr => 'statistics: number of tuples inserted',
-  proname => 'pg_stat_get_tuples_inserted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_inserted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_inserted' },
+  prosrc => 'pg_stat_get_tab_tuples_inserted' },
 { oid => '1932', descr => 'statistics: number of tuples updated',
-  proname => 'pg_stat_get_tuples_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_updated' },
 { oid => '1933', descr => 'statistics: number of tuples deleted',
-  proname => 'pg_stat_get_tuples_deleted', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_deleted', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_deleted' },
+  prosrc => 'pg_stat_get_tab_tuples_deleted' },
 { oid => '1972', descr => 'statistics: number of tuples hot updated',
-  proname => 'pg_stat_get_tuples_hot_updated', provolatile => 's',
+  proname => 'pg_stat_get_tab_tuples_hot_updated', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_tuples_hot_updated' },
+  prosrc => 'pg_stat_get_tab_tuples_hot_updated' },
 { oid => '2878', descr => 'statistics: number of live tuples',
-  proname => 'pg_stat_get_live_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_live_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_live_tuples' },
+  prosrc => 'pg_stat_get_tab_live_tuples' },
 { oid => '2879', descr => 'statistics: number of dead tuples',
-  proname => 'pg_stat_get_dead_tuples', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_dead_tuples', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_dead_tuples' },
+  prosrc => 'pg_stat_get_tab_dead_tuples' },
 { oid => '3177',
   descr => 'statistics: number of tuples changed since last analyze',
-  proname => 'pg_stat_get_mod_since_analyze', provolatile => 's',
+  proname => 'pg_stat_get_tab_mod_since_analyze', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_mod_since_analyze' },
+  prosrc => 'pg_stat_get_tab_mod_since_analyze' },
 { oid => '5053',
   descr => 'statistics: number of tuples inserted since last vacuum',
-  proname => 'pg_stat_get_ins_since_vacuum', provolatile => 's',
+  proname => 'pg_stat_get_tab_ins_since_vacuum', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_ins_since_vacuum' },
+  prosrc => 'pg_stat_get_tab_ins_since_vacuum' },
 { oid => '1934', descr => 'statistics: number of blocks fetched',
-  proname => 'pg_stat_get_blocks_fetched', provolatile => 's',
+  proname => 'pg_stat_get_tab_blocks_fetched', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_fetched' },
+  prosrc => 'pg_stat_get_tab_blocks_fetched' },
+{ oid => '9432', descr => 'statistics: number of blocks fetched',
+  proname => 'pg_stat_get_ind_blocks_fetched', provolatile => 's',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_ind_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_tab_blocks_hit', provolatile => 's', proparallel => 'r',
+  prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_blocks_hit' },
+{ oid => '8354', descr => 'statistics: number of blocks found in cache',
+  proname => 'pg_stat_get_ind_blocks_hit', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_blocks_hit' },
+  prosrc => 'pg_stat_get_ind_blocks_hit' },
 { oid => '2781', descr => 'statistics: last manual vacuum time for a table',
-  proname => 'pg_stat_get_last_vacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_vacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_vacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_vacuum_time' },
 { oid => '2782', descr => 'statistics: last auto vacuum time for a table',
-  proname => 'pg_stat_get_last_autovacuum_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autovacuum_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autovacuum_time' },
+  prosrc => 'pg_stat_get_tab_last_autovacuum_time' },
 { oid => '2783', descr => 'statistics: last manual analyze time for a table',
-  proname => 'pg_stat_get_last_analyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_analyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_analyze_time' },
+  prosrc => 'pg_stat_get_tab_last_analyze_time' },
 { oid => '2784', descr => 'statistics: last auto analyze time for a table',
-  proname => 'pg_stat_get_last_autoanalyze_time', provolatile => 's',
+  proname => 'pg_stat_get_tab_last_autoanalyze_time', provolatile => 's',
   proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_last_autoanalyze_time' },
+  prosrc => 'pg_stat_get_tab_last_autoanalyze_time' },
 { oid => '3054', descr => 'statistics: number of manual vacuums for a table',
-  proname => 'pg_stat_get_vacuum_count', provolatile => 's', proparallel => 'r',
+  proname => 'pg_stat_get_tab_vacuum_count', provolatile => 's', proparallel => 'r',
   prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_vacuum_count' },
+  prosrc => 'pg_stat_get_tab_vacuum_count' },
 { oid => '3055', descr => 'statistics: number of auto vacuums for a table',
-  proname => 'pg_stat_get_autovacuum_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autovacuum_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autovacuum_count' },
+  prosrc => 'pg_stat_get_tab_autovacuum_count' },
 { oid => '3056', descr => 'statistics: number of manual analyzes for a table',
-  proname => 'pg_stat_get_analyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_analyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_analyze_count' },
+  prosrc => 'pg_stat_get_tab_analyze_count' },
 { oid => '3057', descr => 'statistics: number of auto analyzes for a table',
-  proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',
+  proname => 'pg_stat_get_tab_autoanalyze_count', provolatile => 's',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_autoanalyze_count' },
+  prosrc => 'pg_stat_get_tab_autoanalyze_count' },
 { oid => '1936', descr => 'statistics: currently active backend IDs',
   proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',
   provolatile => 's', proparallel => 'r', prorettype => 'int4',
@@ -5719,10 +5743,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_tab_xact_numscans', provolatile => 'v',
+  proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_tab_xact_numscans' },
+{ oid => '8892',
+  descr => 'statistics: number of scans done for index in current transaction',
+  proname => 'pg_stat_get_ind_xact_numscans', provolatile => 'v',
   proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',
-  prosrc => 'pg_stat_get_xact_numscans' },
+  prosrc => 'pg_stat_get_ind_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 a3df8d27c3..a35465bc92 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 autoanalyze_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;
@@ -494,16 +541,15 @@ extern PgStat_BackendFunctionEntry *find_funcstat_entry(Oid func_id);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.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_drop_table(Relation rel);
+extern void pgstat_init_table(Relation rel);
 
-extern void pgstat_init_relation(Relation rel);
-extern void pgstat_assoc_relation(Relation rel);
-extern void pgstat_unlink_relation(Relation rel);
+extern void pgstat_assoc_table(Relation rel);
+extern void pgstat_unlink_table(Relation rel);
 
 extern void pgstat_report_vacuum(Oid tableoid, bool shared,
 								 PgStat_Counter livetuples, PgStat_Counter deadtuples);
@@ -511,53 +557,83 @@ extern void pgstat_report_analyze(Relation rel,
 								  PgStat_Counter livetuples, PgStat_Counter deadtuples,
 								  bool resetcounter);
 
+/*
+ * Functions in pgstat_index.c
+ */
+
+extern void pgstat_create_index(Relation rel);
+extern void pgstat_drop_index(Relation rel);
+extern void pgstat_copy_index_stats(Relation dst, Relation src);
+extern void pgstat_init_index(Relation rel);
+extern void pgstat_assoc_index(Relation rel);
+extern void pgstat_unlink_index(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_table()
+ * and pgstat_init_index().
  */
-#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 +649,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..004e5412ab 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
 {
@@ -550,16 +556,23 @@ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
 
 
 /*
- * Functions in pgstat_relation.c
+ * Functions in pgstat_table.c
  */
 
-extern void AtEOXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit);
-extern void AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
-extern void AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
-extern void PostPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state);
+extern void AtEOXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit);
+extern void AtEOSubXact_PgStat_Tables(PgStat_SubXactStatus *xact_state, bool isCommit, int nestDepth);
+extern void AtPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+extern void PostPrepare_PgStat_Tables(PgStat_SubXactStatus *xact_state);
+
+extern bool pgstat_table_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+extern void pgstat_table_delete_pending_cb(PgStat_EntryRef *entry_ref);
+
+/*
+ * Functions in pgstat_index.c
+ */
 
-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_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+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..494adee88b 100644
--- a/src/test/isolation/expected/stats.out
+++ b/src/test/isolation/expected/stats.out
@@ -2166,14 +2166,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2210,14 +2210,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2236,14 +2236,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2275,14 +2275,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2314,14 +2314,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2369,14 +2369,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2401,14 +2401,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2463,14 +2463,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2495,14 +2495,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2558,14 +2558,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2627,14 +2627,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2688,14 +2688,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2755,14 +2755,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2795,14 +2795,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2841,14 +2841,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2881,14 +2881,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2927,14 +2927,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2968,14 +2968,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3015,14 +3015,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out
index 3854320106..7981747732 100644
--- a/src/test/isolation/expected/stats_1.out
+++ b/src/test/isolation/expected/stats_1.out
@@ -2174,14 +2174,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2218,14 +2218,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2244,14 +2244,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2283,14 +2283,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2322,14 +2322,14 @@ 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_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2377,14 +2377,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2409,14 +2409,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2471,14 +2471,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2503,14 +2503,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2566,14 +2566,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2635,14 +2635,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2698,14 +2698,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2767,14 +2767,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2809,14 +2809,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2857,14 +2857,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2899,14 +2899,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2947,14 +2947,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -2990,14 +2990,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
@@ -3039,14 +3039,14 @@ pg_stat_force_next_flush
 
 step s1_table_stats: 
     SELECT
-        pg_stat_get_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
         pg_stat_get_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 
diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec
index 5b922d788c..53d78f7db1 100644
--- a/src/test/isolation/specs/stats.spec
+++ b/src/test/isolation/specs/stats.spec
@@ -93,14 +93,14 @@ 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_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,
-        pg_stat_get_live_tuples(tso.oid) AS n_live_tup,
-        pg_stat_get_dead_tuples(tso.oid) AS n_dead_tup,
-        pg_stat_get_vacuum_count(tso.oid) AS vacuum_count
+        pg_stat_get_tab_numscans(tso.oid) AS seq_scan,
+        pg_stat_get_tab_tuples_returned(tso.oid) AS seq_tup_read,
+        pg_stat_get_tab_tuples_inserted(tso.oid) AS n_tup_ins,
+        pg_stat_get_tab_tuples_updated(tso.oid) AS n_tup_upd,
+        pg_stat_get_tab_tuples_deleted(tso.oid) AS n_tup_del,
+        pg_stat_get_tab_live_tuples(tso.oid) AS n_live_tup,
+        pg_stat_get_tab_dead_tuples(tso.oid) AS n_dead_tup,
+        pg_stat_get_tab_vacuum_count(tso.oid) AS vacuum_count
     FROM test_stat_oid AS tso
     WHERE tso.name = 'test_stat_tab'
 }
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 fb9f936d43..9c2b813789 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1767,10 +1767,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_ind_numscans(i.oid) AS idx_scan,
+    pg_stat_get_ind_lastscan(i.oid) AS last_idx_scan,
+    pg_stat_get_ind_tuples_returned(i.oid) AS idx_tup_read,
+    pg_stat_get_ind_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)))
@@ -1779,28 +1779,28 @@ 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_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,
-    pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
-    pg_stat_get_live_tuples(c.oid) AS n_live_tup,
-    pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
-    pg_stat_get_mod_since_analyze(c.oid) AS n_mod_since_analyze,
-    pg_stat_get_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
-    pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum,
-    pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum,
-    pg_stat_get_last_analyze_time(c.oid) AS last_analyze,
-    pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze,
-    pg_stat_get_vacuum_count(c.oid) AS vacuum_count,
-    pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count,
-    pg_stat_get_analyze_count(c.oid) AS analyze_count,
-    pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count
+    pg_stat_get_tab_numscans(c.oid) AS seq_scan,
+    pg_stat_get_tab_lastscan(c.oid) AS last_seq_scan,
+    pg_stat_get_tab_tuples_returned(c.oid) AS seq_tup_read,
+    (sum(pg_stat_get_ind_numscans(i.indexrelid)))::bigint AS idx_scan,
+    max(pg_stat_get_ind_lastscan(i.indexrelid)) AS last_idx_scan,
+    ((sum(pg_stat_get_ind_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tab_tuples_fetched(c.oid)) AS idx_tup_fetch,
+    pg_stat_get_tab_tuples_inserted(c.oid) AS n_tup_ins,
+    pg_stat_get_tab_tuples_updated(c.oid) AS n_tup_upd,
+    pg_stat_get_tab_tuples_deleted(c.oid) AS n_tup_del,
+    pg_stat_get_tab_tuples_hot_updated(c.oid) AS n_tup_hot_upd,
+    pg_stat_get_tab_live_tuples(c.oid) AS n_live_tup,
+    pg_stat_get_tab_dead_tuples(c.oid) AS n_dead_tup,
+    pg_stat_get_tab_mod_since_analyze(c.oid) AS n_mod_since_analyze,
+    pg_stat_get_tab_ins_since_vacuum(c.oid) AS n_ins_since_vacuum,
+    pg_stat_get_tab_last_vacuum_time(c.oid) AS last_vacuum,
+    pg_stat_get_tab_last_autovacuum_time(c.oid) AS last_autovacuum,
+    pg_stat_get_tab_last_analyze_time(c.oid) AS last_analyze,
+    pg_stat_get_tab_last_autoanalyze_time(c.oid) AS last_autoanalyze,
+    pg_stat_get_tab_vacuum_count(c.oid) AS vacuum_count,
+    pg_stat_get_tab_autovacuum_count(c.oid) AS autovacuum_count,
+    pg_stat_get_tab_analyze_count(c.oid) AS analyze_count,
+    pg_stat_get_tab_autoanalyze_count(c.oid) AS autoanalyze_count
    FROM ((pg_class c
      LEFT JOIN pg_index i ON ((c.oid = i.indrelid)))
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
@@ -2224,9 +2224,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_tab_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_ind_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,
@@ -2277,8 +2277,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_ind_blocks_fetched(i.oid) - pg_stat_get_ind_blocks_hit(i.oid)) AS idx_blks_read,
+    pg_stat_get_ind_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)))
@@ -2287,31 +2287,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_ind_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(c.oid) - pg_stat_get_tab_blocks_hit(c.oid)) AS heap_blks_read,
+    pg_stat_get_tab_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_tab_blocks_fetched(t.oid) - pg_stat_get_tab_blocks_hit(t.oid)) AS toast_blks_read,
+    pg_stat_get_tab_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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_ind_blocks_fetched(pg_index.indexrelid) - pg_stat_get_ind_blocks_hit(pg_index.indexrelid))))::bigint AS idx_blks_read,
+            (sum(pg_stat_get_ind_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 1d84407a03..d5ee0f4845 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;$$;
@@ -357,17 +468,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
@@ -378,18 +489,18 @@ SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            1
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               1
+(1 row)
+
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
  pg_stat_get_xact_tuples_inserted 
 ----------------------------------
@@ -418,29 +529,29 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
------------------------------
-                           2
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               2
 (1 row)
 
 BEGIN;
@@ -465,23 +576,23 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
-(1 row)
-
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
- pg_stat_get_tuples_inserted 
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_live_tuples 
 -----------------------------
                            0
 (1 row)
 
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
+ pg_stat_get_tab_tuples_inserted 
+---------------------------------
+                               0
+(1 row)
+
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       1
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           1
 (1 row)
 
 BEGIN;
@@ -510,17 +621,17 @@ SELECT pg_stat_force_next_flush();
  
 (1 row)
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -529,17 +640,17 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       3
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           3
 (1 row)
 
 BEGIN;
@@ -548,10 +659,10 @@ DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
- pg_stat_get_live_tuples 
--------------------------
-                       0
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
+ pg_stat_get_tab_live_tuples 
+-----------------------------
+                           0
 (1 row)
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
@@ -1021,21 +1132,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
@@ -1051,14 +1162,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
@@ -1073,7 +1184,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
@@ -1081,7 +1192,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
@@ -1089,7 +1200,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
@@ -1097,7 +1208,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
@@ -1105,7 +1216,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 b4d6753c71..1e9ae74b2a 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
 ---
@@ -218,15 +289,15 @@ SELECT 'drop_stats_test_subxact'::regclass::oid AS drop_stats_test_subxact_oid \
 
 SELECT pg_stat_force_next_flush();
 
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 DROP TABLE drop_stats_test;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_oid);
 
 -- check that rollback protects against having stats dropped and that local
 -- modifications don't pose a problem
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
@@ -235,12 +306,12 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 ROLLBACK;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- transactional drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_xact DEFAULT VALUES;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
@@ -248,11 +319,11 @@ DROP TABLE drop_stats_test_xact;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_xact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_xact_oid);
-SELECT pg_stat_get_tuples_inserted(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_xact_oid);
+SELECT pg_stat_get_tab_tuples_inserted(:drop_stats_test_xact_oid);
 
 -- savepoint rollback (2 levels)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 INSERT INTO drop_stats_test_subxact DEFAULT VALUES;
 SAVEPOINT sp1;
@@ -264,27 +335,27 @@ ROLLBACK TO SAVEPOINT sp2;
 SELECT pg_stat_get_xact_tuples_inserted(:drop_stats_test_subxact_oid);
 COMMIT;
 SELECT pg_stat_force_next_flush();
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- savepoint rolback (1 level)
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 ROLLBACK TO SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 -- and now actually drop
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 BEGIN;
 SAVEPOINT sp1;
 DROP TABLE drop_stats_test_subxact;
 SAVEPOINT sp2;
 RELEASE SAVEPOINT sp1;
 COMMIT;
-SELECT pg_stat_get_live_tuples(:drop_stats_test_subxact_oid);
+SELECT pg_stat_get_tab_live_tuples(:drop_stats_test_subxact_oid);
 
 DROP TABLE trunc_stats_test, trunc_stats_test1, trunc_stats_test2, trunc_stats_test3, trunc_stats_test4;
 DROP TABLE prevstats;
@@ -493,40 +564,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 60c71d05fe..3be9c0e1ff 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
@@ -2026,6 +2027,7 @@ PgStat_FetchConsistency
 PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
+PgStat_IndexStatus
 PgStat_Kind
 PgStat_KindInfo
 PgStat_LocalState


^ permalink  raw  reply  [nested|flat] 17+ messages in thread

* Re: Split index and table statistics into different types of stats
@ 2024-01-25 08:36 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 17+ messages in thread

From: Bertrand Drouvot @ 2024-01-25 08:36 UTC (permalink / raw)
  To: Andres Freund <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; Gregory Stark (as CFM) <[email protected]>; Michael Paquier <[email protected]>; vignesh C <[email protected]>; Bharath Rupireddy <[email protected]>; Melanie Plageman <[email protected]>; PostgreSQL Hackers <[email protected]>

Hi,

On Tue, Nov 14, 2023 at 09:04:03AM +0100, Drouvot, Bertrand wrote:
> On 11/13/23 9:44 PM, Andres Freund wrote:
> > Hi,
> > 
> > It's not nice from a layering POV that we need this level of awareness in
> > bufmgr.c.  I wonder if this is an argument for first splitting out stats like
> > blocks_hit, blocks_fetched into something like "relfilenode stats" - they're
> > agnostic of the relkind.
> 
> Thanks for looking at it! Yeah I think that would make a lot of sense
> to track some stats per relfilenode.
> 
> > There aren't that many such stats right now,
> > admittedly, but I think we'll want to also track dirtied, written blocks on a
> > per relation basis once we can (i.e. we key the relevant stats by relfilenode
> > instead of oid, so we can associate stats when writing out buffers).
> > 
> > 
> 
> Agree. Then, I think that would make sense to start this effort before the
> split index/table one. I can work on a per relfilenode stat patch first.
> 
> Does this patch ordering make sense to you?
> 
> 1) Introduce per relfilenode stats
> 2) Split index and table stats

Just a quick update on this: I had a chat with Andres at pgconf.eu and we agreed
on the above ordering so that:

1) I started working on relfilenode stats (I hope to be able to provide a POC
patch soon).

2) The CF entry [1] status related to this thread has been changed to "Waiting
on Author".

[1]: https://commitfest.postgresql.org/47/4792/

Regards,

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





^ permalink  raw  reply  [nested|flat] 17+ messages in thread


end of thread, other threads:[~2024-01-25 08:36 UTC | newest]

Thread overview: 17+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-10-31 13:14 Split index and table statistics into different types of stats Drouvot, Bertrand <[email protected]>
2022-10-31 16:42 ` Drouvot, Bertrand <[email protected]>
2022-11-01 00:30 ` Andres Freund <[email protected]>
2022-11-02 08:58   ` Drouvot, Bertrand <[email protected]>
2022-11-04 20:51     ` Melanie Plageman <[email protected]>
2022-11-14 10:36       ` Drouvot, Bertrand <[email protected]>
2022-11-15 09:48         ` Drouvot, Bertrand <[email protected]>
2022-11-16 20:12           ` Andres Freund <[email protected]>
2022-11-18 11:18             ` Drouvot, Bertrand <[email protected]>
2022-11-20 23:19               ` Andres Freund <[email protected]>
2022-11-21 13:32                 ` Drouvot, Bertrand <[email protected]>
2022-11-22 06:19                   ` Bharath Rupireddy <[email protected]>
2022-11-22 07:12                     ` Drouvot, Bertrand <[email protected]>
2022-12-06 19:11                       ` Drouvot, Bertrand <[email protected]>
2022-12-07 10:11                         ` Drouvot, Bertrand <[email protected]>
2022-12-10 09:54                           ` Drouvot, Bertrand <[email protected]>
2024-01-25 08:36 Re: Split index and table statistics into different types of stats Bertrand Drouvot <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox