@@ -160,6 +160,20 @@ typedef struct TabStatusArray
160
160
161
161
static TabStatusArray * pgStatTabList = NULL ;
162
162
163
+ /*
164
+ * pgStatTabHash entry
165
+ */
166
+ typedef struct TabStatHashEntry
167
+ {
168
+ Oid t_id ;
169
+ PgStat_TableStatus * tsa_entry ;
170
+ } TabStatHashEntry ;
171
+
172
+ /*
173
+ * Hash table for O(1) t_id -> tsa_entry lookup
174
+ */
175
+ static HTAB * pgStatTabHash = NULL ;
176
+
163
177
/*
164
178
* Backends store per-function info that's waiting to be sent to the collector
165
179
* in this hash table (indexed by function OID).
@@ -817,6 +831,14 @@ pgstat_report_stat(bool force)
817
831
tsa -> tsa_used = 0 ;
818
832
}
819
833
834
+ /*
835
+ * pgStatTabHash is outdated on this point so we have to clean it,
836
+ * hash_destroy() will remove hash memory context, allocated in
837
+ * make_sure_stat_tab_initialized()
838
+ */
839
+ hash_destroy (pgStatTabHash );
840
+ pgStatTabHash = NULL ;
841
+
820
842
/*
821
843
* Send partial messages. Make sure that any pending xact commit/abort
822
844
* gets counted, even if there are no table stats to send.
@@ -1661,60 +1683,88 @@ pgstat_initstats(Relation rel)
1661
1683
rel -> pgstat_info = get_tabstat_entry (rel_id , rel -> rd_rel -> relisshared );
1662
1684
}
1663
1685
1686
+ /*
1687
+ * Make sure pgStatTabList and pgStatTabHash are initialized.
1688
+ */
1689
+ static void
1690
+ make_sure_stat_tab_initialized ()
1691
+ {
1692
+ HASHCTL ctl ;
1693
+ MemoryContext new_ctx ;
1694
+
1695
+ if (!pgStatTabList )
1696
+ {
1697
+ /* This is first time procedure is called */
1698
+ pgStatTabList = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1699
+ sizeof (TabStatusArray ));
1700
+ }
1701
+
1702
+ if (pgStatTabHash )
1703
+ return ;
1704
+
1705
+ /* Hash table was freed or never existed. */
1706
+
1707
+ new_ctx = AllocSetContextCreate (
1708
+ TopMemoryContext ,
1709
+ "PGStatLookupHashTableContext" ,
1710
+ ALLOCSET_DEFAULT_SIZES );
1711
+
1712
+ memset (& ctl , 0 , sizeof (ctl ));
1713
+ ctl .keysize = sizeof (Oid );
1714
+ ctl .entrysize = sizeof (TabStatHashEntry );
1715
+ ctl .hcxt = new_ctx ;
1716
+
1717
+ pgStatTabHash = hash_create ("pgstat t_id to tsa_entry lookup hash table" ,
1718
+ TABSTAT_QUANTUM , & ctl , HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
1719
+ }
1720
+
1664
1721
/*
1665
1722
* get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
1666
1723
*/
1667
1724
static PgStat_TableStatus *
1668
1725
get_tabstat_entry (Oid rel_id , bool isshared )
1669
1726
{
1727
+ TabStatHashEntry * hash_entry ;
1670
1728
PgStat_TableStatus * entry ;
1671
1729
TabStatusArray * tsa ;
1672
- TabStatusArray * prev_tsa ;
1673
- int i ;
1730
+ bool found ;
1731
+
1732
+ make_sure_stat_tab_initialized ();
1674
1733
1675
1734
/*
1676
- * Search the already-used tabstat slots for this relation .
1735
+ * Find an entry or create a new one .
1677
1736
*/
1678
- prev_tsa = NULL ;
1679
- for (tsa = pgStatTabList ; tsa != NULL ; prev_tsa = tsa , tsa = tsa -> tsa_next )
1737
+ hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_ENTER , & found );
1738
+ if (found )
1739
+ return hash_entry -> tsa_entry ;
1740
+
1741
+ /*
1742
+ * `hash_entry` was just created and now we have to fill it.
1743
+ * First make sure there is a free space in a last element of pgStatTabList.
1744
+ */
1745
+ tsa = pgStatTabList ;
1746
+ while (tsa -> tsa_used == TABSTAT_QUANTUM )
1680
1747
{
1681
- for ( i = 0 ; i < tsa -> tsa_used ; i ++ )
1748
+ if ( tsa -> tsa_next == NULL )
1682
1749
{
1683
- entry = & tsa -> tsa_entries [i ];
1684
- if (entry -> t_id == rel_id )
1685
- return entry ;
1750
+ tsa -> tsa_next = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1751
+ sizeof (TabStatusArray ));
1686
1752
}
1687
1753
1688
- if (tsa -> tsa_used < TABSTAT_QUANTUM )
1689
- {
1690
- /*
1691
- * It must not be present, but we found a free slot instead. Fine,
1692
- * let's use this one. We assume the entry was already zeroed,
1693
- * either at creation or after last use.
1694
- */
1695
- entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
1696
- entry -> t_id = rel_id ;
1697
- entry -> t_shared = isshared ;
1698
- return entry ;
1699
- }
1754
+ tsa = tsa -> tsa_next ;
1700
1755
}
1701
1756
1702
1757
/*
1703
- * We ran out of tabstat slots, so allocate more. Be sure they're zeroed.
1704
- */
1705
- tsa = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1706
- sizeof (TabStatusArray ));
1707
- if (prev_tsa )
1708
- prev_tsa -> tsa_next = tsa ;
1709
- else
1710
- pgStatTabList = tsa ;
1711
-
1712
- /*
1713
- * Use the first entry of the new TabStatusArray.
1758
+ * Add an entry.
1714
1759
*/
1715
1760
entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
1716
1761
entry -> t_id = rel_id ;
1717
1762
entry -> t_shared = isshared ;
1763
+
1764
+ /*
1765
+ * Add a corresponding entry to pgStatTabHash.
1766
+ */
1767
+ hash_entry -> tsa_entry = entry ;
1718
1768
return entry ;
1719
1769
}
1720
1770
@@ -1726,22 +1776,19 @@ get_tabstat_entry(Oid rel_id, bool isshared)
1726
1776
PgStat_TableStatus *
1727
1777
find_tabstat_entry (Oid rel_id )
1728
1778
{
1729
- PgStat_TableStatus * entry ;
1730
- TabStatusArray * tsa ;
1731
- int i ;
1779
+ TabStatHashEntry * hash_entry ;
1732
1780
1733
- for (tsa = pgStatTabList ; tsa != NULL ; tsa = tsa -> tsa_next )
1734
- {
1735
- for (i = 0 ; i < tsa -> tsa_used ; i ++ )
1736
- {
1737
- entry = & tsa -> tsa_entries [i ];
1738
- if (entry -> t_id == rel_id )
1739
- return entry ;
1740
- }
1741
- }
1781
+ /*
1782
+ * There are no entries at all.
1783
+ */
1784
+ if (!pgStatTabHash )
1785
+ return NULL ;
1742
1786
1743
- /* Not present */
1744
- return NULL ;
1787
+ hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_FIND , NULL );
1788
+ if (!hash_entry )
1789
+ return NULL ;
1790
+
1791
+ return hash_entry -> tsa_entry ;
1745
1792
}
1746
1793
1747
1794
/*
0 commit comments