@@ -173,6 +173,20 @@ typedef struct TabStatusArray
173173
174174static TabStatusArray * pgStatTabList = NULL ;
175175
176+ /*
177+ * pgStatTabHash entry
178+ */
179+ typedef struct TabStatHashEntry
180+ {
181+ Oid t_id ;
182+ PgStat_TableStatus * tsa_entry ;
183+ } TabStatHashEntry ;
184+
185+ /*
186+ * Hash table for O(1) t_id -> tsa_entry lookup
187+ */
188+ static HTAB * pgStatTabHash = NULL ;
189+
176190/*
177191 * Backends store per-function info that's waiting to be sent to the collector
178192 * in this hash table (indexed by function OID).
@@ -840,6 +854,14 @@ pgstat_report_stat(bool force)
840854 tsa -> tsa_used = 0 ;
841855 }
842856
857+ /*
858+ * pgStatTabHash is outdated on this point so we have to clean it,
859+ * hash_destroy() will remove hash memory context, allocated in
860+ * make_sure_stat_tab_initialized()
861+ */
862+ hash_destroy (pgStatTabHash );
863+ pgStatTabHash = NULL ;
864+
843865 /*
844866 * Send partial messages. Make sure that any pending xact commit/abort
845867 * gets counted, even if there are no table stats to send.
@@ -1684,60 +1706,88 @@ pgstat_initstats(Relation rel)
16841706 rel -> pgstat_info = get_tabstat_entry (rel_id , rel -> rd_rel -> relisshared );
16851707}
16861708
1709+ /*
1710+ * Make sure pgStatTabList and pgStatTabHash are initialized.
1711+ */
1712+ static void
1713+ make_sure_stat_tab_initialized ()
1714+ {
1715+ HASHCTL ctl ;
1716+ MemoryContext new_ctx ;
1717+
1718+ if (!pgStatTabList )
1719+ {
1720+ /* This is first time procedure is called */
1721+ pgStatTabList = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1722+ sizeof (TabStatusArray ));
1723+ }
1724+
1725+ if (pgStatTabHash )
1726+ return ;
1727+
1728+ /* Hash table was freed or never existed. */
1729+
1730+ new_ctx = AllocSetContextCreate (
1731+ TopMemoryContext ,
1732+ "PGStatLookupHashTableContext" ,
1733+ ALLOCSET_DEFAULT_SIZES );
1734+
1735+ memset (& ctl , 0 , sizeof (ctl ));
1736+ ctl .keysize = sizeof (Oid );
1737+ ctl .entrysize = sizeof (TabStatHashEntry );
1738+ ctl .hcxt = new_ctx ;
1739+
1740+ pgStatTabHash = hash_create ("pgstat t_id to tsa_entry lookup hash table" ,
1741+ TABSTAT_QUANTUM , & ctl , HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
1742+ }
1743+
16871744/*
16881745 * get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
16891746 */
16901747static PgStat_TableStatus *
16911748get_tabstat_entry (Oid rel_id , bool isshared )
16921749{
1750+ TabStatHashEntry * hash_entry ;
16931751 PgStat_TableStatus * entry ;
16941752 TabStatusArray * tsa ;
1695- TabStatusArray * prev_tsa ;
1696- int i ;
1753+ bool found ;
1754+
1755+ make_sure_stat_tab_initialized ();
16971756
16981757 /*
1699- * Search the already-used tabstat slots for this relation .
1758+ * Find an entry or create a new one .
17001759 */
1701- prev_tsa = NULL ;
1702- for (tsa = pgStatTabList ; tsa != NULL ; prev_tsa = tsa , tsa = tsa -> tsa_next )
1760+ hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_ENTER , & found );
1761+ if (found )
1762+ return hash_entry -> tsa_entry ;
1763+
1764+ /*
1765+ * `hash_entry` was just created and now we have to fill it.
1766+ * First make sure there is a free space in a last element of pgStatTabList.
1767+ */
1768+ tsa = pgStatTabList ;
1769+ while (tsa -> tsa_used == TABSTAT_QUANTUM )
17031770 {
1704- for ( i = 0 ; i < tsa -> tsa_used ; i ++ )
1771+ if ( tsa -> tsa_next == NULL )
17051772 {
1706- entry = & tsa -> tsa_entries [i ];
1707- if (entry -> t_id == rel_id )
1708- return entry ;
1773+ tsa -> tsa_next = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1774+ sizeof (TabStatusArray ));
17091775 }
17101776
1711- if (tsa -> tsa_used < TABSTAT_QUANTUM )
1712- {
1713- /*
1714- * It must not be present, but we found a free slot instead. Fine,
1715- * let's use this one. We assume the entry was already zeroed,
1716- * either at creation or after last use.
1717- */
1718- entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
1719- entry -> t_id = rel_id ;
1720- entry -> t_shared = isshared ;
1721- return entry ;
1722- }
1777+ tsa = tsa -> tsa_next ;
17231778 }
17241779
17251780 /*
1726- * We ran out of tabstat slots, so allocate more. Be sure they're zeroed.
1727- */
1728- tsa = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1729- sizeof (TabStatusArray ));
1730- if (prev_tsa )
1731- prev_tsa -> tsa_next = tsa ;
1732- else
1733- pgStatTabList = tsa ;
1734-
1735- /*
1736- * Use the first entry of the new TabStatusArray.
1781+ * Add an entry.
17371782 */
17381783 entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
17391784 entry -> t_id = rel_id ;
17401785 entry -> t_shared = isshared ;
1786+
1787+ /*
1788+ * Add a corresponding entry to pgStatTabHash.
1789+ */
1790+ hash_entry -> tsa_entry = entry ;
17411791 return entry ;
17421792}
17431793
@@ -1749,22 +1799,19 @@ get_tabstat_entry(Oid rel_id, bool isshared)
17491799PgStat_TableStatus *
17501800find_tabstat_entry (Oid rel_id )
17511801{
1752- PgStat_TableStatus * entry ;
1753- TabStatusArray * tsa ;
1754- int i ;
1802+ TabStatHashEntry * hash_entry ;
17551803
1756- for (tsa = pgStatTabList ; tsa != NULL ; tsa = tsa -> tsa_next )
1757- {
1758- for (i = 0 ; i < tsa -> tsa_used ; i ++ )
1759- {
1760- entry = & tsa -> tsa_entries [i ];
1761- if (entry -> t_id == rel_id )
1762- return entry ;
1763- }
1764- }
1804+ /*
1805+ * There are no entries at all.
1806+ */
1807+ if (!pgStatTabHash )
1808+ return NULL ;
17651809
1766- /* Not present */
1767- return NULL ;
1810+ hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_FIND , NULL );
1811+ if (!hash_entry )
1812+ return NULL ;
1813+
1814+ return hash_entry -> tsa_entry ;
17681815}
17691816
17701817/*
0 commit comments