On Wed, Aug 12, 2026 at 11:52:16AM +0000, Bertrand Drouvot wrote:
> I've a few comments:

Thanks for the review.

> I agree that keeping a single Relation.pgstat_info pointer makes sense.
> That said, IIUC, it does not require both kinds to use the same allocation
> size.
> 
> I wonder if we could keep the single pointer design while using a small common
> header in separate table and index pending structures? That would allow
> Relation.pgstat_info to point to the common header while using a different
> pending_size for each kind.

TBH, I find this a bit unattractive because it reduces code clarity.
There is a bit of memory wasted for pending index data for indexes due
to the fact that the union's size is calculated based on its largest 
number.  It's nothing new, the split just makes cleaner the handling
of the fields, in terms of which stats kind can touch each part.

> The table and index specific functions and macros assume that the relation
> has the expected kind. That assumption existed before this patch, but both
> kinds previously used PgStat_TableStatus, so those accesses still referred to
> fields that existed in the pending object.
> 
> With the new union, a wrong call can modify an unrelated member. For example,
> pgstat_count_index_tuples() on a table updates idx.tuples_returned, which
> overlaps the tab.trans pointer.
> 
> The current callers look correct, but worth adding assertions for the expected
> kind in those functions and macros?

Hmm.  That sounds like a good idea at the end, based on the stats
kind, before touching any relevant field.  That enforces the union
policy on a caller basis.

> -typedef struct PgStat_TableXactStatus
> +typedef struct PgStat_RelXactStatus
>  {
> 
> PgStat_RelationStatus is common to tables and indexes, but 
> PgStat_RelXactStatus
> tracks transactional tuple changes for tables only.
> 
> Would keeping PgStat_TableXactStatus be less ambiguous here? It would also
> match add_tabstat_xact_level() and ensure_tabstat_xact_level().

Works for me.  Perhaps I was just too much enthusiastic with this part
of the rename.  :p

Updated patch attached.
--
Michael
From ef0d8330f43d8e965d6a6e376c6b19f31483ffa2 Mon Sep 17 00:00:00 2001
From: Michael Paquier <[email protected]>
Date: Thu, 13 Aug 2026 07:33:17 +0900
Subject: [PATCH v17] Refactor PgStat_TableStatus to new PgStat_RelationStatus

This new structure is split depending on the relkind it deals with:
- PGSTAT_KIND_RELATION, for tables.
- PGSTAT_KIND_INDEX, for indexes.

This change makes the barrier cleaner between the handling of tables and
indexes, by being able to track precisely what are the counters used by
one or the other for pending data.  Using a common ground for both eases
the tracking of Relations in the relcache.
---
 src/include/pgstat.h                         | 112 +++++--
 src/include/utils/pgstat_internal.h          |   2 +-
 src/include/utils/rel.h                      |   2 +-
 src/backend/utils/activity/pgstat.c          |   4 +-
 src/backend/utils/activity/pgstat_index.c    |  30 +-
 src/backend/utils/activity/pgstat_relation.c | 308 ++++++++++---------
 src/backend/utils/adt/pgstatfuncs.c          |  13 +-
 src/backend/utils/cache/relcache.c           |   2 +-
 src/tools/pgindent/typedefs.list             |   3 +-
 9 files changed, 278 insertions(+), 198 deletions(-)

diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 9c29dbe2c44c..204782fd630c 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -125,14 +125,12 @@ typedef struct PgStat_BackendSubEntry
  * of pg_memory_is_all_zeros() to detect whether there are any stats updates
  * to apply.
  *
- * It is a component of PgStat_TableStatus (within-backend state).
+ * It is a component of PgStat_RelationStatus (within-backend state, for
+ * table data).
  *
- * Note: for a table, 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.
+ * 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.
  *
  * tuples_inserted/updated/deleted/hot_updated/newpage_updated count attempted
  * actions, regardless of whether the transaction committed.  
delta_live_tuples,
@@ -163,28 +161,63 @@ typedef struct PgStat_TableCounts
 } PgStat_TableCounts;
 
 /* ----------
- * PgStat_TableStatus                  Per-table status within a backend
+ * PgStat_IndexCounts                  Per-index pending event counters
+ *
+ * Note: 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.
+ *
+ * It is a component of PgStat_RelationStatus (within-backend state, for
+ * index data).
+ * ----------
+ */
+typedef struct PgStat_IndexCounts
+{
+       PgStat_Counter numscans;
+       PgStat_Counter tuples_returned;
+       PgStat_Counter tuples_fetched;
+       PgStat_Counter blocks_fetched;
+       PgStat_Counter blocks_hit;
+} PgStat_IndexCounts;
+
+/* ----------
+ * PgStat_RelationStatus                       Per-relation pending 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_TableStatus.  However, delta_live_tuples,
+ * directly in the PgStat_RelationStatus.  However, delta_live_tuples,
  * delta_dead_tuples, and changed_tuples must be derived from event counts
  * with awareness of whether the transaction or subtransaction committed or
  * aborted.  Hence, we also keep a stack of per-(sub)transaction status
  * records for every table modified in the current transaction.  At commit
  * or abort, we propagate tuples_inserted/updated/deleted up to the
- * parent subtransaction level, or out to the parent PgStat_TableStatus,
+ * parent subtransaction level, or out to the parent PgStat_RelationStatus,
  * as appropriate.
+ *
+ * 'kind' tracks the stats kind we are dealing with, for table or index
+ * pending data.
  * ----------
  */
-typedef struct PgStat_TableStatus
+typedef struct PgStat_RelationStatus
 {
-       Oid                     id;                             /* table's OID 
*/
-       bool            shared;                 /* is it a shared catalog? */
-       struct PgStat_TableXactStatus *trans;   /* lowest subxact's counts */
-       PgStat_TableCounts counts;      /* event counts to be sent */
+       PgStat_Kind kind;                       /* PGSTAT_KIND_RELATION or 
PGSTAT_KIND_INDEX */
        Relation        relation;               /* rel that is using this entry 
*/
-} PgStat_TableStatus;
+       union
+       {
+               /* table counters */
+               struct
+               {
+                       Oid                     id;             /* table's OID 
*/
+                       bool            shared; /* is it a shared catalog? */
+                       struct PgStat_TableXactStatus *trans;   /* lowest 
subxact's counts */
+                       PgStat_TableCounts counts;      /* event counts to be 
sent */
+               }                       tab;
+
+               /* index counters */
+               PgStat_IndexCounts idx;
+       };
+} PgStat_RelationStatus;
 
 /* ----------
  * PgStat_TableXactStatus              Per-table, per-subtransaction status
@@ -204,7 +237,7 @@ typedef struct PgStat_TableXactStatus
        int                     nest_level;             /* subtransaction nest 
level */
        /* links to other structs for same relation: */
        struct PgStat_TableXactStatus *upper;   /* next higher subxact if any */
-       PgStat_TableStatus *parent; /* per-table status */
+       PgStat_RelationStatus *parent;  /* per-table status */
        /* structs of same subxact level are linked here: */
        struct PgStat_TableXactStatus *next;    /* next of same subxact */
 } PgStat_TableXactStatus;
@@ -744,37 +777,64 @@ extern void pgstat_report_analyze(Relation rel,
 #define pgstat_count_heap_scan(rel)                                            
                        \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.numscans++;                  
                \
+               {                                                               
                                                        \
+                       Assert((rel)->pgstat_info->kind == 
PGSTAT_KIND_RELATION); \
+                       (rel)->pgstat_info->tab.counts.numscans++;              
                \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_heap_getnext(rel)                                         
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.tuples_returned++;           
        \
+               {                                                               
                                                        \
+                       Assert((rel)->pgstat_info->kind == 
PGSTAT_KIND_RELATION); \
+                       (rel)->pgstat_info->tab.counts.tuples_returned++;       
        \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_heap_fetch(rel)                                           
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.tuples_fetched++;            
        \
+               {                                                               
                                                        \
+                       if ((rel)->pgstat_info->kind == PGSTAT_KIND_INDEX)      
        \
+                               (rel)->pgstat_info->idx.tuples_fetched++;       
                \
+                       else                                                    
                                                \
+                               
(rel)->pgstat_info->tab.counts.tuples_fetched++;                \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_index_scan(rel)                                           
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.numscans++;                  
                \
+               {                                                               
                                                        \
+                       Assert((rel)->pgstat_info->kind == PGSTAT_KIND_INDEX);  
\
+                       (rel)->pgstat_info->idx.numscans++;                     
                        \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_index_tuples(rel, n)                                      
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.tuples_returned += (n);      
        \
+               {                                                               
                                                        \
+                       Assert((rel)->pgstat_info->kind == PGSTAT_KIND_INDEX);  
\
+                       (rel)->pgstat_info->idx.tuples_returned += (n);         
        \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_buffer_read(rel)                                          
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.blocks_fetched++;            
        \
+               {                                                               
                                                        \
+                       if ((rel)->pgstat_info->kind == PGSTAT_KIND_INDEX)      
        \
+                               (rel)->pgstat_info->idx.blocks_fetched++;       
                \
+                       else                                                    
                                                \
+                               
(rel)->pgstat_info->tab.counts.blocks_fetched++;                \
+               }                                                               
                                                        \
        } while (0)
 #define pgstat_count_buffer_hit(rel)                                           
                \
        do {                                                                    
                                                \
                if (pgstat_should_count_relation(rel))                          
                \
-                       (rel)->pgstat_info->counts.blocks_hit++;                
                \
+               {                                                               
                                                        \
+                       if ((rel)->pgstat_info->kind == PGSTAT_KIND_INDEX)      
        \
+                               (rel)->pgstat_info->idx.blocks_hit++;           
                \
+                       else                                                    
                                                \
+                               (rel)->pgstat_info->tab.counts.blocks_hit++;    
                \
+               }                                                               
                                                        \
        } while (0)
 
 extern void pgstat_count_heap_insert(Relation rel, PgStat_Counter n);
@@ -792,8 +852,8 @@ extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid 
relid);
 extern PgStat_StatTabEntry *pgstat_fetch_stat_tabentry_ext(bool shared,
                                                                                
                                   Oid reloid,
                                                                                
                                   bool *may_free);
-extern PgStat_TableStatus *find_tabstat_entry(Oid rel_id);
-extern PgStat_TableStatus *find_tabstat_entry_kind(PgStat_Kind kind, Oid 
rel_id);
+extern PgStat_RelationStatus *find_relstat_entry_kind(PgStat_Kind kind,
+                                                                               
                          Oid rel_id);
 
 extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry(Oid relid);
 extern PgStat_StatIdxEntry *pgstat_fetch_stat_idxentry_ext(bool shared,
diff --git a/src/include/utils/pgstat_internal.h 
b/src/include/utils/pgstat_internal.h
index 1ef98620e1c0..14369e59a1cc 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -215,7 +215,7 @@ typedef struct PgStat_SubXactStatus
 
        /*
         * Tuple insertion/deletion counts for an open transaction can't be
-        * propagated into PgStat_TableStatus counters until we know if it is
+        * propagated into PgStat_RelationStatus counters until we know if it is
         * going to commit or abort.  Hence, we keep these counts in per-subxact
         * structs that live in TopTransactionContext.  This data structure is
         * designed on the assumption that subxacts won't usually modify very 
many
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 89c159b133fa..674ac5a1cf46 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -252,7 +252,7 @@ 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 */
+       struct PgStat_RelationStatus *pgstat_info;      /* statistics 
collection area */
 } RelationData;
 
 
diff --git a/src/backend/utils/activity/pgstat.c 
b/src/backend/utils/activity/pgstat.c
index fe1637e20956..4615f6101069 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -310,7 +310,7 @@ static const PgStat_KindInfo 
pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
                .shared_size = sizeof(PgStatShared_Relation),
                .shared_data_off = offsetof(PgStatShared_Relation, stats),
                .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
-               .pending_size = sizeof(PgStat_TableStatus),
+               .pending_size = sizeof(PgStat_RelationStatus),
 
                .flush_pending_cb = pgstat_relation_flush_cb,
                .delete_pending_cb = pgstat_relation_delete_pending_cb,
@@ -326,7 +326,7 @@ static const PgStat_KindInfo 
pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE]
                .shared_size = sizeof(PgStatShared_Index),
                .shared_data_off = offsetof(PgStatShared_Index, stats),
                .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats),
-               .pending_size = sizeof(PgStat_TableStatus),
+               .pending_size = sizeof(PgStat_RelationStatus),
 
                .flush_pending_cb = pgstat_index_flush_cb,
                .delete_pending_cb = pgstat_index_delete_pending_cb,
diff --git a/src/backend/utils/activity/pgstat_index.c 
b/src/backend/utils/activity/pgstat_index.c
index ce7a7f0f56c7..a1f9a4c6ac13 100644
--- a/src/backend/utils/activity/pgstat_index.c
+++ b/src/backend/utils/activity/pgstat_index.c
@@ -35,21 +35,21 @@ bool
 pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 {
        Oid                     dboid;
-       PgStat_TableStatus *lstats; /* pending stats entry */
+       PgStat_RelationStatus *lstats;  /* pending stats entry */
        PgStatShared_Index *shidxstats;
        PgStat_StatIdxEntry *idxentry;  /* index entry of shared stats */
        PgStat_StatDBEntry *dbentry;    /* pending database entry */
 
        dboid = entry_ref->shared_entry->key.dboid;
-       lstats = (PgStat_TableStatus *) entry_ref->pending;
+       lstats = (PgStat_RelationStatus *) entry_ref->pending;
        shidxstats = (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 (pg_memory_is_all_zeros(&lstats->counts,
-                                                          sizeof(struct 
PgStat_TableCounts)))
+       if (pg_memory_is_all_zeros(&lstats->idx,
+                                                          sizeof(struct 
PgStat_IndexCounts)))
                return true;
 
        if (!pgstat_lock_entry(entry_ref, nowait))
@@ -58,27 +58,27 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        /* Add the values to the shared entry. */
        idxentry = &shidxstats->stats;
 
-       idxentry->numscans += lstats->counts.numscans;
-       if (lstats->counts.numscans)
+       idxentry->numscans += lstats->idx.numscans;
+       if (lstats->idx.numscans)
        {
                TimestampTz t = GetCurrentTransactionStopTimestamp();
 
                if (t > idxentry->lastscan)
                        idxentry->lastscan = t;
        }
-       idxentry->tuples_returned += lstats->counts.tuples_returned;
-       idxentry->tuples_fetched += lstats->counts.tuples_fetched;
-       idxentry->blocks_fetched += lstats->counts.blocks_fetched;
-       idxentry->blocks_hit += lstats->counts.blocks_hit;
+       idxentry->tuples_returned += lstats->idx.tuples_returned;
+       idxentry->tuples_fetched += lstats->idx.tuples_fetched;
+       idxentry->blocks_fetched += lstats->idx.blocks_fetched;
+       idxentry->blocks_hit += lstats->idx.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->counts.tuples_returned;
-       dbentry->tuples_fetched += lstats->counts.tuples_fetched;
-       dbentry->blocks_fetched += lstats->counts.blocks_fetched;
-       dbentry->blocks_hit += lstats->counts.blocks_hit;
+       dbentry->tuples_returned += lstats->idx.tuples_returned;
+       dbentry->tuples_fetched += lstats->idx.tuples_fetched;
+       dbentry->blocks_fetched += lstats->idx.blocks_fetched;
+       dbentry->blocks_hit += lstats->idx.blocks_hit;
 
        return true;
 }
@@ -89,7 +89,7 @@ pgstat_index_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 void
 pgstat_index_delete_pending_cb(PgStat_EntryRef *entry_ref)
 {
-       PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending;
+       PgStat_RelationStatus *pending = (PgStat_RelationStatus *) 
entry_ref->pending;
 
        if (pending->relation)
                pgstat_unlink_relation(pending->relation);
diff --git a/src/backend/utils/activity/pgstat_relation.c 
b/src/backend/utils/activity/pgstat_relation.c
index beea9188773b..1221ad0c6444 100644
--- a/src/backend/utils/activity/pgstat_relation.c
+++ b/src/backend/utils/activity/pgstat_relation.c
@@ -42,10 +42,10 @@ typedef struct TwoPhasePgStatRecord
 } TwoPhasePgStatRecord;
 
 
-static PgStat_TableStatus *pgstat_prep_relation_pending(PgStat_Kind kind,
-                                                                               
                                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_RelationStatus *pgstat_prep_relation_pending(PgStat_Kind kind,
+                                                                               
                                   Oid rel_id, bool isshared);
+static void add_tabstat_xact_level(PgStat_RelationStatus *pgstat_info, int 
nest_level);
+static void ensure_tabstat_xact_level(PgStat_RelationStatus *pgstat_info);
 static void save_truncdrop_counters(PgStat_TableXactStatus *trans, bool 
is_drop);
 static void restore_truncdrop_counters(PgStat_TableXactStatus *trans);
 
@@ -177,7 +177,7 @@ pgstat_assoc_relation(Relation rel)
 
        kind = pgstat_get_relation_kind(rel->rd_rel->relkind);
 
-       /* find or make the PgStat_TableStatus entry, and update link */
+       /* find or make the PgStat_RelationStatus entry, and update link */
        rel->pgstat_info = pgstat_prep_relation_pending(kind,
                                                                                
                        RelationGetRelid(rel),
                                                                                
                        rel->rd_rel->relisshared);
@@ -226,7 +226,7 @@ void
 pgstat_drop_relation(Relation rel)
 {
        int                     nest_level = GetCurrentTransactionNestLevel();
-       PgStat_TableStatus *pgstat_info;
+       PgStat_RelationStatus *pgstat_info;
        PgStat_Kind kind = pgstat_get_relation_kind(rel->rd_rel->relkind);
 
        pgstat_drop_transactional(kind,
@@ -239,15 +239,20 @@ pgstat_drop_relation(Relation rel)
        /*
         * Transactionally set counters to 0. That ensures that accesses to
         * pg_stat_xact_all_tables inside the transaction show 0.
+        *
+        * Indexes have no transactional counters, so leave.
         */
        pgstat_info = rel->pgstat_info;
-       if (pgstat_info->trans &&
-               pgstat_info->trans->nest_level == nest_level)
+       if (pgstat_info->kind == PGSTAT_KIND_INDEX)
+               return;
+
+       if (pgstat_info->tab.trans &&
+               pgstat_info->tab.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(pgstat_info->tab.trans, true);
+               pgstat_info->tab.trans->tuples_inserted = 0;
+               pgstat_info->tab.trans->tuples_updated = 0;
+               pgstat_info->tab.trans->tuples_deleted = 0;
        }
 }
 
@@ -357,13 +362,14 @@ pgstat_report_analyze(Relation rel,
        {
                PgStat_TableXactStatus *trans;
 
-               for (trans = rel->pgstat_info->trans; trans; trans = 
trans->upper)
+               Assert(rel->pgstat_info->kind == PGSTAT_KIND_RELATION);
+               for (trans = rel->pgstat_info->tab.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->counts.delta_dead_tuples;
+               deadtuples -= rel->pgstat_info->tab.counts.delta_dead_tuples;
                /* Since ANALYZE's counts are estimates, we could have 
underflowed */
                livetuples = Max(livetuples, 0);
                deadtuples = Max(deadtuples, 0);
@@ -422,10 +428,12 @@ pgstat_count_heap_insert(Relation rel, PgStat_Counter n)
 {
        if (pgstat_should_count_relation(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_RelationStatus *pgstat_info = rel->pgstat_info;
+
+               Assert(pgstat_info->kind == PGSTAT_KIND_RELATION);
 
                ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_inserted += n;
+               pgstat_info->tab.trans->tuples_inserted += n;
        }
 }
 
@@ -439,19 +447,21 @@ pgstat_count_heap_update(Relation rel, bool hot, bool 
newpage)
 
        if (pgstat_should_count_relation(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_RelationStatus *pgstat_info = rel->pgstat_info;
+
+               Assert(pgstat_info->kind == PGSTAT_KIND_RELATION);
 
                ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_updated++;
+               pgstat_info->tab.trans->tuples_updated++;
 
                /*
                 * tuples_hot_updated and tuples_newpage_updated counters are
                 * nontransactional, so just advance them
                 */
                if (hot)
-                       pgstat_info->counts.tuples_hot_updated++;
+                       pgstat_info->tab.counts.tuples_hot_updated++;
                else if (newpage)
-                       pgstat_info->counts.tuples_newpage_updated++;
+                       pgstat_info->tab.counts.tuples_newpage_updated++;
        }
 }
 
@@ -463,10 +473,12 @@ pgstat_count_heap_delete(Relation rel)
 {
        if (pgstat_should_count_relation(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_RelationStatus *pgstat_info = rel->pgstat_info;
+
+               Assert(pgstat_info->kind == PGSTAT_KIND_RELATION);
 
                ensure_tabstat_xact_level(pgstat_info);
-               pgstat_info->trans->tuples_deleted++;
+               pgstat_info->tab.trans->tuples_deleted++;
        }
 }
 
@@ -478,13 +490,15 @@ pgstat_count_truncate(Relation rel)
 {
        if (pgstat_should_count_relation(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_RelationStatus *pgstat_info = rel->pgstat_info;
+
+               Assert(pgstat_info->kind == PGSTAT_KIND_RELATION);
 
                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;
+               save_truncdrop_counters(pgstat_info->tab.trans, false);
+               pgstat_info->tab.trans->tuples_inserted = 0;
+               pgstat_info->tab.trans->tuples_updated = 0;
+               pgstat_info->tab.trans->tuples_deleted = 0;
        }
 }
 
@@ -501,9 +515,11 @@ pgstat_update_heap_dead_tuples(Relation rel, int delta)
 {
        if (pgstat_should_count_relation(rel))
        {
-               PgStat_TableStatus *pgstat_info = rel->pgstat_info;
+               PgStat_RelationStatus *pgstat_info = rel->pgstat_info;
 
-               pgstat_info->counts.delta_dead_tuples -= delta;
+               Assert(pgstat_info->kind == PGSTAT_KIND_RELATION);
+
+               pgstat_info->tab.counts.delta_dead_tuples -= delta;
        }
 }
 
@@ -534,9 +550,9 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, 
bool *may_free)
 }
 
 /*
- * find any existing PgStat_TableStatus entry for rel
+ * find any existing PgStat_RelationStatus entry for rel and kind
  *
- * Find any existing PgStat_TableStatus entry for rel_id in the current
+ * Find any existing PgStat_RelationStatus entry for rel_id in the current
  * database. If not found, try finding from shared tables.
  *
  * If an entry is found, copy it and increment the copy's counters with their
@@ -545,62 +561,60 @@ pgstat_fetch_stat_tabentry_ext(bool shared, Oid reloid, 
bool *may_free)
  *
  * If no entry found, return NULL, don't create a new one.
  */
-PgStat_TableStatus *
-find_tabstat_entry(Oid rel_id)
-{
-       return find_tabstat_entry_kind(PGSTAT_KIND_RELATION, rel_id);
-}
-
-/*
- * Same as find_tabstat_entry but for a specific stats kind.
- */
-PgStat_TableStatus *
-find_tabstat_entry_kind(PgStat_Kind kind, Oid rel_id)
+PgStat_RelationStatus *
+find_relstat_entry_kind(PgStat_Kind kind, Oid rel_id)
 {
        PgStat_EntryRef *entry_ref;
        PgStat_TableXactStatus *trans;
-       PgStat_TableStatus *tabentry = NULL;
-       PgStat_TableStatus *tablestatus = NULL;
+       PgStat_RelationStatus *relentry = NULL;
+       PgStat_RelationStatus *relstatus = NULL;
 
        entry_ref = pgstat_fetch_pending_entry(kind, MyDatabaseId, rel_id);
        if (!entry_ref)
        {
                entry_ref = pgstat_fetch_pending_entry(kind, InvalidOid, 
rel_id);
                if (!entry_ref)
-                       return tablestatus;
+                       return relstatus;
        }
 
-       tabentry = (PgStat_TableStatus *) entry_ref->pending;
-       tablestatus = palloc_object(PgStat_TableStatus);
-       *tablestatus = *tabentry;
+       relentry = (PgStat_RelationStatus *) entry_ref->pending;
+       relstatus = palloc_object(PgStat_RelationStatus);
+       *relstatus = *relentry;
 
        /*
-        * Reset tablestatus->trans in the copy of PgStat_TableStatus as it may
-        * point to a shared memory area.  Its data is saved below, so removing 
it
-        * does not matter.
+        * For index entries, just return the copy.  There is no transactional
+        * data.
         */
-       tablestatus->trans = NULL;
+       if (kind == PGSTAT_KIND_INDEX)
+               return relstatus;
+
+       /*
+        * Reset relstatus->tab.trans in the copy of PgStat_RelationStatus as it
+        * may point to a shared memory area.  Its data is saved below, so
+        * removing it does not matter.
+        */
+       relstatus->tab.trans = NULL;
 
        /*
         * Live subtransaction counts are not included yet.  This is not a hot
         * code path so reconcile tuples_inserted, tuples_updated and
         * tuples_deleted even if the caller may not be interested in this data.
         */
-       for (trans = tabentry->trans; trans != NULL; trans = trans->upper)
+       for (trans = relentry->tab.trans; trans != NULL; trans = trans->upper)
        {
-               tablestatus->counts.tuples_inserted += trans->tuples_inserted;
-               tablestatus->counts.tuples_updated += trans->tuples_updated;
-               tablestatus->counts.tuples_deleted += trans->tuples_deleted;
+               relstatus->tab.counts.tuples_inserted += trans->tuples_inserted;
+               relstatus->tab.counts.tuples_updated += trans->tuples_updated;
+               relstatus->tab.counts.tuples_deleted += trans->tuples_deleted;
        }
 
-       return tablestatus;
+       return relstatus;
 }
 
 /*
  * Perform relation stats specific end-of-transaction work. Helper for
  * AtEOXact_PgStat.
  *
- * Transfer transactional insert/update counts into the base tabstat entries.
+ * Transfer transactional insert/update counts into the base relstat entries.
  * We don't bother to free any of the transactional state, since it's all in
  * TopTransactionContext and will go away anyway.
  */
@@ -611,47 +625,47 @@ AtEOXact_PgStat_Relations(PgStat_SubXactStatus 
*xact_state, bool isCommit)
 
        for (trans = xact_state->first; trans != NULL; trans = trans->next)
        {
-               PgStat_TableStatus *tabstat;
+               PgStat_RelationStatus *relstat;
 
                Assert(trans->nest_level == 1);
                Assert(trans->upper == NULL);
-               tabstat = trans->parent;
-               Assert(tabstat->trans == trans);
+               relstat = trans->parent;
+               Assert(relstat->tab.trans == trans);
                /* restore pre-truncate/drop stats (if any) in case of aborted 
xact */
                if (!isCommit)
                        restore_truncdrop_counters(trans);
                /* count attempted actions regardless of commit/abort */
-               tabstat->counts.tuples_inserted += trans->tuples_inserted;
-               tabstat->counts.tuples_updated += trans->tuples_updated;
-               tabstat->counts.tuples_deleted += trans->tuples_deleted;
+               relstat->tab.counts.tuples_inserted += trans->tuples_inserted;
+               relstat->tab.counts.tuples_updated += trans->tuples_updated;
+               relstat->tab.counts.tuples_deleted += trans->tuples_deleted;
                if (isCommit)
                {
-                       tabstat->counts.truncdropped = trans->truncdropped;
+                       relstat->tab.counts.truncdropped = trans->truncdropped;
                        if (trans->truncdropped)
                        {
                                /* forget live/dead stats seen by backend thus 
far */
-                               tabstat->counts.delta_live_tuples = 0;
-                               tabstat->counts.delta_dead_tuples = 0;
+                               relstat->tab.counts.delta_live_tuples = 0;
+                               relstat->tab.counts.delta_dead_tuples = 0;
                        }
                        /* insert adds a live tuple, delete removes one */
-                       tabstat->counts.delta_live_tuples +=
+                       relstat->tab.counts.delta_live_tuples +=
                                trans->tuples_inserted - trans->tuples_deleted;
                        /* update and delete each create a dead tuple */
-                       tabstat->counts.delta_dead_tuples +=
+                       relstat->tab.counts.delta_dead_tuples +=
                                trans->tuples_updated + trans->tuples_deleted;
                        /* insert, update, delete each count as one change 
event */
-                       tabstat->counts.changed_tuples +=
+                       relstat->tab.counts.changed_tuples +=
                                trans->tuples_inserted + trans->tuples_updated +
                                trans->tuples_deleted;
                }
                else
                {
                        /* inserted tuples are dead, deleted tuples are 
unaffected */
-                       tabstat->counts.delta_dead_tuples +=
+                       relstat->tab.counts.delta_dead_tuples +=
                                trans->tuples_inserted + trans->tuples_updated;
                        /* an aborted xact generates no changed_tuple events */
                }
-               tabstat->trans = NULL;
+               relstat->tab.trans = NULL;
        }
 }
 
@@ -670,12 +684,12 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus 
*xact_state, bool isCommit, in
 
        for (trans = xact_state->first; trans != NULL; trans = next_trans)
        {
-               PgStat_TableStatus *tabstat;
+               PgStat_RelationStatus *relstat;
 
                next_trans = trans->next;
                Assert(trans->nest_level == nestDepth);
-               tabstat = trans->parent;
-               Assert(tabstat->trans == trans);
+               relstat = trans->parent;
+               Assert(relstat->tab.trans == trans);
 
                if (isCommit)
                {
@@ -696,7 +710,7 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus 
*xact_state, bool isCommit, in
                                        trans->upper->tuples_updated += 
trans->tuples_updated;
                                        trans->upper->tuples_deleted += 
trans->tuples_deleted;
                                }
-                               tabstat->trans = trans->upper;
+                               relstat->tab.trans = trans->upper;
                                pfree(trans);
                        }
                        else
@@ -720,20 +734,20 @@ AtEOSubXact_PgStat_Relations(PgStat_SubXactStatus 
*xact_state, bool isCommit, in
                else
                {
                        /*
-                        * On abort, update top-level tabstat counts, then 
forget the
+                        * On abort, update top-level relstat counts, then 
forget the
                         * subtransaction
                         */
 
                        /* first restore values obliterated by truncate/drop */
                        restore_truncdrop_counters(trans);
                        /* count attempted actions regardless of commit/abort */
-                       tabstat->counts.tuples_inserted += 
trans->tuples_inserted;
-                       tabstat->counts.tuples_updated += trans->tuples_updated;
-                       tabstat->counts.tuples_deleted += trans->tuples_deleted;
+                       relstat->tab.counts.tuples_inserted += 
trans->tuples_inserted;
+                       relstat->tab.counts.tuples_updated += 
trans->tuples_updated;
+                       relstat->tab.counts.tuples_deleted += 
trans->tuples_deleted;
                        /* inserted tuples are dead, deleted tuples are 
unaffected */
-                       tabstat->counts.delta_dead_tuples +=
+                       relstat->tab.counts.delta_dead_tuples +=
                                trans->tuples_inserted + trans->tuples_updated;
-                       tabstat->trans = trans->upper;
+                       relstat->tab.trans = trans->upper;
                        pfree(trans);
                }
        }
@@ -750,13 +764,13 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus 
*xact_state)
 
        for (trans = xact_state->first; trans != NULL; trans = trans->next)
        {
-               PgStat_TableStatus *tabstat PG_USED_FOR_ASSERTS_ONLY;
+               PgStat_RelationStatus *relstat PG_USED_FOR_ASSERTS_ONLY;
                TwoPhasePgStatRecord record;
 
                Assert(trans->nest_level == 1);
                Assert(trans->upper == NULL);
-               tabstat = trans->parent;
-               Assert(tabstat->trans == trans);
+               relstat = trans->parent;
+               Assert(relstat->tab.trans == trans);
 
                record.tuples_inserted = trans->tuples_inserted;
                record.tuples_updated = trans->tuples_updated;
@@ -764,8 +778,8 @@ AtPrepare_PgStat_Relations(PgStat_SubXactStatus *xact_state)
                record.inserted_pre_truncdrop = trans->inserted_pre_truncdrop;
                record.updated_pre_truncdrop = trans->updated_pre_truncdrop;
                record.deleted_pre_truncdrop = trans->deleted_pre_truncdrop;
-               record.id = tabstat->id;
-               record.shared = tabstat->shared;
+               record.id = relstat->tab.id;
+               record.shared = relstat->tab.shared;
                record.truncdropped = trans->truncdropped;
 
                RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
@@ -788,10 +802,10 @@ PostPrepare_PgStat_Relations(PgStat_SubXactStatus 
*xact_state)
 
        for (trans = xact_state->first; trans != NULL; trans = trans->next)
        {
-               PgStat_TableStatus *tabstat;
+               PgStat_RelationStatus *relstat;
 
-               tabstat = trans->parent;
-               tabstat->trans = NULL;
+               relstat = trans->parent;
+               relstat->tab.trans = NULL;
        }
 }
 
@@ -805,27 +819,27 @@ pgstat_twophase_postcommit(FullTransactionId fxid, uint16 
info,
                                                   void *recdata, uint32 len)
 {
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
-       PgStat_TableStatus *pgstat_info;
+       PgStat_RelationStatus *pgstat_info;
 
-       /* Find or create a tabstat entry for the rel */
+       /* Find or create a relstat entry for the rel */
        pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, 
rec->id, rec->shared);
 
        /* Same math as in AtEOXact_PgStat, commit case */
-       pgstat_info->counts.tuples_inserted += rec->tuples_inserted;
-       pgstat_info->counts.tuples_updated += rec->tuples_updated;
-       pgstat_info->counts.tuples_deleted += rec->tuples_deleted;
-       pgstat_info->counts.truncdropped = rec->truncdropped;
+       pgstat_info->tab.counts.tuples_inserted += rec->tuples_inserted;
+       pgstat_info->tab.counts.tuples_updated += rec->tuples_updated;
+       pgstat_info->tab.counts.tuples_deleted += rec->tuples_deleted;
+       pgstat_info->tab.counts.truncdropped = rec->truncdropped;
        if (rec->truncdropped)
        {
                /* forget live/dead stats seen by backend thus far */
-               pgstat_info->counts.delta_live_tuples = 0;
-               pgstat_info->counts.delta_dead_tuples = 0;
+               pgstat_info->tab.counts.delta_live_tuples = 0;
+               pgstat_info->tab.counts.delta_dead_tuples = 0;
        }
-       pgstat_info->counts.delta_live_tuples +=
+       pgstat_info->tab.counts.delta_live_tuples +=
                rec->tuples_inserted - rec->tuples_deleted;
-       pgstat_info->counts.delta_dead_tuples +=
+       pgstat_info->tab.counts.delta_dead_tuples +=
                rec->tuples_updated + rec->tuples_deleted;
-       pgstat_info->counts.changed_tuples +=
+       pgstat_info->tab.counts.changed_tuples +=
                rec->tuples_inserted + rec->tuples_updated +
                rec->tuples_deleted;
 }
@@ -841,9 +855,9 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint16 
info,
                                                  void *recdata, uint32 len)
 {
        TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
-       PgStat_TableStatus *pgstat_info;
+       PgStat_RelationStatus *pgstat_info;
 
-       /* Find or create a tabstat entry for the rel */
+       /* Find or create a relstat entry for the rel */
        pgstat_info = pgstat_prep_relation_pending(PGSTAT_KIND_RELATION, 
rec->id, rec->shared);
 
        /* Same math as in AtEOXact_PgStat, abort case */
@@ -853,10 +867,10 @@ pgstat_twophase_postabort(FullTransactionId fxid, uint16 
info,
                rec->tuples_updated = rec->updated_pre_truncdrop;
                rec->tuples_deleted = rec->deleted_pre_truncdrop;
        }
-       pgstat_info->counts.tuples_inserted += rec->tuples_inserted;
-       pgstat_info->counts.tuples_updated += rec->tuples_updated;
-       pgstat_info->counts.tuples_deleted += rec->tuples_deleted;
-       pgstat_info->counts.delta_dead_tuples +=
+       pgstat_info->tab.counts.tuples_inserted += rec->tuples_inserted;
+       pgstat_info->tab.counts.tuples_updated += rec->tuples_updated;
+       pgstat_info->tab.counts.tuples_deleted += rec->tuples_deleted;
+       pgstat_info->tab.counts.delta_dead_tuples +=
                rec->tuples_inserted + rec->tuples_updated;
 }
 
@@ -873,17 +887,17 @@ bool
 pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 {
        Oid                     dboid;
-       PgStat_TableStatus *lstats; /* pending stats entry  */
+       PgStat_RelationStatus *lstats;  /* pending stats entry  */
        PgStatShared_Relation *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;
+       lstats = (PgStat_RelationStatus *) entry_ref->pending;
        shtabstats = (PgStatShared_Relation *) entry_ref->shared_stats;
 
        /* ignore entries that didn't accumulate any actual counts */
-       if (pg_memory_is_all_zeros(&lstats->counts,
+       if (pg_memory_is_all_zeros(&lstats->tab.counts,
                                                           sizeof(struct 
PgStat_TableCounts)))
                return true;
 
@@ -893,35 +907,35 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
        /* add the values to the shared entry. */
        tabentry = &shtabstats->stats;
 
-       tabentry->numscans += lstats->counts.numscans;
-       if (lstats->counts.numscans)
+       tabentry->numscans += lstats->tab.counts.numscans;
+       if (lstats->tab.counts.numscans)
        {
                TimestampTz t = GetCurrentTransactionStopTimestamp();
 
                if (t > tabentry->lastscan)
                        tabentry->lastscan = t;
        }
-       tabentry->tuples_returned += lstats->counts.tuples_returned;
-       tabentry->tuples_fetched += lstats->counts.tuples_fetched;
-       tabentry->tuples_inserted += lstats->counts.tuples_inserted;
-       tabentry->tuples_updated += lstats->counts.tuples_updated;
-       tabentry->tuples_deleted += lstats->counts.tuples_deleted;
-       tabentry->tuples_hot_updated += lstats->counts.tuples_hot_updated;
-       tabentry->tuples_newpage_updated += 
lstats->counts.tuples_newpage_updated;
+       tabentry->tuples_returned += lstats->tab.counts.tuples_returned;
+       tabentry->tuples_fetched += lstats->tab.counts.tuples_fetched;
+       tabentry->tuples_inserted += lstats->tab.counts.tuples_inserted;
+       tabentry->tuples_updated += lstats->tab.counts.tuples_updated;
+       tabentry->tuples_deleted += lstats->tab.counts.tuples_deleted;
+       tabentry->tuples_hot_updated += lstats->tab.counts.tuples_hot_updated;
+       tabentry->tuples_newpage_updated += 
lstats->tab.counts.tuples_newpage_updated;
 
        /*
         * If table was truncated/dropped, first reset the live/dead counters.
         */
-       if (lstats->counts.truncdropped)
+       if (lstats->tab.counts.truncdropped)
        {
                tabentry->live_tuples = 0;
                tabentry->dead_tuples = 0;
                tabentry->ins_since_vacuum = 0;
        }
 
-       tabentry->live_tuples += lstats->counts.delta_live_tuples;
-       tabentry->dead_tuples += lstats->counts.delta_dead_tuples;
-       tabentry->mod_since_analyze += lstats->counts.changed_tuples;
+       tabentry->live_tuples += lstats->tab.counts.delta_live_tuples;
+       tabentry->dead_tuples += lstats->tab.counts.delta_dead_tuples;
+       tabentry->mod_since_analyze += lstats->tab.counts.changed_tuples;
 
        /*
         * Using tuples_inserted to update ins_since_vacuum does mean that we'll
@@ -930,10 +944,10 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
         * triggering for inserts more often than they maybe should, which is
         * probably not going to be common enough to be too concerned about 
here.
         */
-       tabentry->ins_since_vacuum += lstats->counts.tuples_inserted;
+       tabentry->ins_since_vacuum += lstats->tab.counts.tuples_inserted;
 
-       tabentry->blocks_fetched += lstats->counts.blocks_fetched;
-       tabentry->blocks_hit += lstats->counts.blocks_hit;
+       tabentry->blocks_fetched += lstats->tab.counts.blocks_fetched;
+       tabentry->blocks_hit += lstats->tab.counts.blocks_hit;
 
        /* Clamp live_tuples in case of negative delta_live_tuples */
        tabentry->live_tuples = Max(tabentry->live_tuples, 0);
@@ -944,13 +958,13 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
 
        /* The entry was successfully flushed, add the same to database stats */
        dbentry = pgstat_prep_database_pending(dboid);
-       dbentry->tuples_returned += lstats->counts.tuples_returned;
-       dbentry->tuples_fetched += lstats->counts.tuples_fetched;
-       dbentry->tuples_inserted += lstats->counts.tuples_inserted;
-       dbentry->tuples_updated += lstats->counts.tuples_updated;
-       dbentry->tuples_deleted += lstats->counts.tuples_deleted;
-       dbentry->blocks_fetched += lstats->counts.blocks_fetched;
-       dbentry->blocks_hit += lstats->counts.blocks_hit;
+       dbentry->tuples_returned += lstats->tab.counts.tuples_returned;
+       dbentry->tuples_fetched += lstats->tab.counts.tuples_fetched;
+       dbentry->tuples_inserted += lstats->tab.counts.tuples_inserted;
+       dbentry->tuples_updated += lstats->tab.counts.tuples_updated;
+       dbentry->tuples_deleted += lstats->tab.counts.tuples_deleted;
+       dbentry->blocks_fetched += lstats->tab.counts.blocks_fetched;
+       dbentry->blocks_hit += lstats->tab.counts.blocks_hit;
 
        return true;
 }
@@ -958,7 +972,7 @@ pgstat_relation_flush_cb(PgStat_EntryRef *entry_ref, bool 
nowait)
 void
 pgstat_relation_delete_pending_cb(PgStat_EntryRef *entry_ref)
 {
-       PgStat_TableStatus *pending = (PgStat_TableStatus *) entry_ref->pending;
+       PgStat_RelationStatus *pending = (PgStat_RelationStatus *) 
entry_ref->pending;
 
        if (pending->relation)
                pgstat_unlink_relation(pending->relation);
@@ -971,21 +985,25 @@ pgstat_relation_reset_timestamp_cb(PgStatShared_Common 
*header, TimestampTz ts)
 }
 
 /*
- * Find or create a PgStat_TableStatus entry for rel. New entry is created and
+ * Find or create a PgStat_RelationStatus entry for rel. New entry is created 
and
  * initialized if not exists.
  */
-static PgStat_TableStatus *
+static PgStat_RelationStatus *
 pgstat_prep_relation_pending(PgStat_Kind kind, Oid rel_id, bool isshared)
 {
        PgStat_EntryRef *entry_ref;
-       PgStat_TableStatus *pending;
+       PgStat_RelationStatus *pending;
 
        entry_ref = pgstat_prep_pending_entry(kind,
                                                                                
  isshared ? InvalidOid : MyDatabaseId,
                                                                                
  rel_id, NULL);
        pending = entry_ref->pending;
-       pending->id = rel_id;
-       pending->shared = isshared;
+       pending->kind = kind;
+       if (kind != PGSTAT_KIND_INDEX)
+       {
+               pending->tab.id = rel_id;
+               pending->tab.shared = isshared;
+       }
 
        return pending;
 }
@@ -994,7 +1012,7 @@ pgstat_prep_relation_pending(PgStat_Kind kind, 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_RelationStatus *pgstat_info, int nest_level)
 {
        PgStat_SubXactStatus *xact_state;
        PgStat_TableXactStatus *trans;
@@ -1010,23 +1028,23 @@ 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->upper = pgstat_info->tab.trans;
        trans->parent = pgstat_info;
        trans->next = xact_state->first;
        xact_state->first = trans;
-       pgstat_info->trans = trans;
+       pgstat_info->tab.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_RelationStatus *pgstat_info)
 {
        int                     nest_level = GetCurrentTransactionNestLevel();
 
-       if (pgstat_info->trans == NULL ||
-               pgstat_info->trans->nest_level != nest_level)
+       if (pgstat_info->tab.trans == NULL ||
+               pgstat_info->tab.trans->nest_level != nest_level)
                add_tabstat_xact_level(pgstat_info, nest_level);
 }
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c 
b/src/backend/utils/adt/pgstatfuncs.c
index 946025f39ed0..0d47d745c18f 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1928,12 +1928,13 @@ CppConcat(pg_stat_get_xact_,stat)(PG_FUNCTION_ARGS)     
        \
 {                                                                              
                                \
        Oid         relid = PG_GETARG_OID(0);                           \
        int64       result;                                                     
                \
-       PgStat_TableStatus *tabentry;                                           
\
+       PgStat_RelationStatus *tabentry;                                        
        \
                                                                                
                                \
-       if ((tabentry = find_tabstat_entry(relid)) == NULL)     \
+       if ((tabentry = find_relstat_entry_kind(PGSTAT_KIND_RELATION, \
+                                                                               
        relid)) == NULL)        \
                result = 0;                                                     
                        \
        else                                                                    
                        \
-               result = (int64) (tabentry->counts.stat);               \
+               result = (int64) (tabentry->tab.counts.stat);           \
                                                                                
                                \
        PG_RETURN_INT64(result);                                                
        \
 }
@@ -1977,13 +1978,13 @@ CppConcat(pg_stat_get_xact_idx_,stat)(PG_FUNCTION_ARGS) 
\
 {                                                                              
                                \
        Oid         relid = PG_GETARG_OID(0);                           \
        int64       result;                                                     
                \
-       PgStat_TableStatus *tabentry;                                           
\
+       PgStat_RelationStatus *tabentry;                                        
        \
                                                                                
                                \
-       tabentry = find_tabstat_entry_kind(PGSTAT_KIND_INDEX, relid); \
+       tabentry = find_relstat_entry_kind(PGSTAT_KIND_INDEX, relid); \
        if (!tabentry)                                                          
                \
                result = 0;                                                     
                        \
        else                                                                    
                        \
-               result = (int64) (tabentry->counts.stat);               \
+               result = (int64) (tabentry->idx.stat);          \
                                                                                
                                \
        PG_RETURN_INT64(result);                                                
        \
 }
diff --git a/src/backend/utils/cache/relcache.c 
b/src/backend/utils/cache/relcache.c
index 19c4ff6e75e1..9abbaeab4a9d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2760,7 +2760,7 @@ RelationRebuildRelation(Relation relation)
                /* toast OID override must be preserved */
                SWAPFIELD(Oid, rd_toastoid);
                /* pgstat_info / enabled must be preserved */
-               SWAPFIELD(struct PgStat_TableStatus *, pgstat_info);
+               SWAPFIELD(struct PgStat_RelationStatus *, pgstat_info);
                SWAPFIELD(bool, pgstat_enabled);
                /* preserve old partition key if we have one */
                if (keep_partkey)
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8e3213954d84..e6e816664aa1 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2348,6 +2348,7 @@ PgStat_FunctionCallUsage
 PgStat_FunctionCounts
 PgStat_HashKey
 PgStat_IO
+PgStat_IndexCounts
 PgStat_KindInfo
 PgStat_LocalState
 PgStat_Lock
@@ -2355,6 +2356,7 @@ PgStat_LockEntry
 PgStat_PendingDroppedStatsItem
 PgStat_PendingIO
 PgStat_PendingLock
+PgStat_RelationStatus
 PgStat_SLRUStats
 PgStat_ShmemControl
 PgStat_Snapshot
@@ -2370,7 +2372,6 @@ PgStat_StatTabEntry
 PgStat_StatsFileOp
 PgStat_SubXactStatus
 PgStat_TableCounts
-PgStat_TableStatus
 PgStat_TableXactStatus
 PgStat_WalCounters
 PgStat_WalStats
-- 
2.55.0

Attachment: signature.asc
Description: PGP signature

Reply via email to