@@ -174,7 +174,7 @@ typedef struct TabStatusArray
174174static TabStatusArray * pgStatTabList = NULL ;
175175
176176/*
177- * pgStatTabHash entry
177+ * pgStatTabHash entry: map from relation OID to PgStat_TableStatus pointer
178178 */
179179typedef struct TabStatHashEntry
180180{
@@ -805,6 +805,17 @@ pgstat_report_stat(bool force)
805805 return ;
806806 last_report = now ;
807807
808+ /*
809+ * Destroy pgStatTabHash before we start invalidating PgStat_TableEntry
810+ * entries it points to. (Should we fail partway through the loop below,
811+ * it's okay to have removed the hashtable already --- the only
812+ * consequence is we'd get multiple entries for the same table in the
813+ * pgStatTabList, and that's safe.)
814+ */
815+ if (pgStatTabHash )
816+ hash_destroy (pgStatTabHash );
817+ pgStatTabHash = NULL ;
818+
808819 /*
809820 * Scan through the TabStatusArray struct(s) to find tables that actually
810821 * have counts, and build messages to send. We have to separate shared
@@ -855,14 +866,6 @@ pgstat_report_stat(bool force)
855866 tsa -> tsa_used = 0 ;
856867 }
857868
858- /*
859- * pgStatTabHash is outdated on this point so we have to clean it,
860- * hash_destroy() will remove hash memory context, allocated in
861- * make_sure_stat_tab_initialized()
862- */
863- hash_destroy (pgStatTabHash );
864- pgStatTabHash = NULL ;
865-
866869 /*
867870 * Send partial messages. Make sure that any pending xact commit/abort
868871 * gets counted, even if there are no table stats to send.
@@ -1707,41 +1710,6 @@ pgstat_initstats(Relation rel)
17071710 rel -> pgstat_info = get_tabstat_entry (rel_id , rel -> rd_rel -> relisshared );
17081711}
17091712
1710- /*
1711- * Make sure pgStatTabList and pgStatTabHash are initialized.
1712- */
1713- static void
1714- make_sure_stat_tab_initialized ()
1715- {
1716- HASHCTL ctl ;
1717- MemoryContext new_ctx ;
1718-
1719- if (!pgStatTabList )
1720- {
1721- /* This is first time procedure is called */
1722- pgStatTabList = (TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1723- sizeof (TabStatusArray ));
1724- }
1725-
1726- if (pgStatTabHash )
1727- return ;
1728-
1729- /* Hash table was freed or never existed. */
1730-
1731- new_ctx = AllocSetContextCreate (
1732- TopMemoryContext ,
1733- "PGStatLookupHashTableContext" ,
1734- ALLOCSET_DEFAULT_SIZES );
1735-
1736- memset (& ctl , 0 , sizeof (ctl ));
1737- ctl .keysize = sizeof (Oid );
1738- ctl .entrysize = sizeof (TabStatHashEntry );
1739- ctl .hcxt = new_ctx ;
1740-
1741- pgStatTabHash = hash_create ("pgstat t_id to tsa_entry lookup hash table" ,
1742- TABSTAT_QUANTUM , & ctl , HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
1743- }
1744-
17451713/*
17461714 * get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
17471715 */
@@ -1753,65 +1721,101 @@ get_tabstat_entry(Oid rel_id, bool isshared)
17531721 TabStatusArray * tsa ;
17541722 bool found ;
17551723
1756- make_sure_stat_tab_initialized ();
1724+ /*
1725+ * Create hash table if we don't have it already.
1726+ */
1727+ if (pgStatTabHash == NULL )
1728+ {
1729+ HASHCTL ctl ;
1730+
1731+ memset (& ctl , 0 , sizeof (ctl ));
1732+ ctl .keysize = sizeof (Oid );
1733+ ctl .entrysize = sizeof (TabStatHashEntry );
1734+
1735+ pgStatTabHash = hash_create ("pgstat TabStatusArray lookup hash table" ,
1736+ TABSTAT_QUANTUM ,
1737+ & ctl ,
1738+ HASH_ELEM | HASH_BLOBS );
1739+ }
17571740
17581741 /*
17591742 * Find an entry or create a new one.
17601743 */
17611744 hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_ENTER , & found );
1762- if (found )
1745+ if (!found )
1746+ {
1747+ /* initialize new entry with null pointer */
1748+ hash_entry -> tsa_entry = NULL ;
1749+ }
1750+
1751+ /*
1752+ * If entry is already valid, we're done.
1753+ */
1754+ if (hash_entry -> tsa_entry )
17631755 return hash_entry -> tsa_entry ;
17641756
17651757 /*
1766- * `hash_entry` was just created and now we have to fill it.
1767- * First make sure there is a free space in a last element of pgStatTabList.
1758+ * Locate the first pgStatTabList entry with free space, making a new list
1759+ * entry if needed. Note that we could get an OOM failure here, but if so
1760+ * we have left the hashtable and the list in a consistent state.
17681761 */
1769- tsa = pgStatTabList ;
1770- while (tsa -> tsa_used == TABSTAT_QUANTUM )
1762+ if (pgStatTabList == NULL )
17711763 {
1772- if ( tsa -> tsa_next == NULL )
1773- {
1774- tsa -> tsa_next = ( TabStatusArray * ) MemoryContextAllocZero (TopMemoryContext ,
1775- sizeof (TabStatusArray ));
1776- }
1764+ /* Set up first pgStatTabList entry */
1765+ pgStatTabList = ( TabStatusArray * )
1766+ MemoryContextAllocZero (TopMemoryContext ,
1767+ sizeof (TabStatusArray ));
1768+ }
17771769
1770+ tsa = pgStatTabList ;
1771+ while (tsa -> tsa_used >= TABSTAT_QUANTUM )
1772+ {
1773+ if (tsa -> tsa_next == NULL )
1774+ tsa -> tsa_next = (TabStatusArray * )
1775+ MemoryContextAllocZero (TopMemoryContext ,
1776+ sizeof (TabStatusArray ));
17781777 tsa = tsa -> tsa_next ;
17791778 }
17801779
17811780 /*
1782- * Add an entry.
1781+ * Allocate a PgStat_TableStatus entry within this list entry. We assume
1782+ * the entry was already zeroed, either at creation or after last use.
17831783 */
17841784 entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
17851785 entry -> t_id = rel_id ;
17861786 entry -> t_shared = isshared ;
17871787
17881788 /*
1789- * Add a corresponding entry to pgStatTabHash.
1789+ * Now we can fill the entry in pgStatTabHash.
17901790 */
17911791 hash_entry -> tsa_entry = entry ;
1792+
17921793 return entry ;
17931794}
17941795
17951796/*
17961797 * find_tabstat_entry - find any existing PgStat_TableStatus entry for rel
17971798 *
17981799 * If no entry, return NULL, don't create a new one
1800+ *
1801+ * Note: if we got an error in the most recent execution of pgstat_report_stat,
1802+ * it's possible that an entry exists but there's no hashtable entry for it.
1803+ * That's okay, we'll treat this case as "doesn't exist".
17991804 */
18001805PgStat_TableStatus *
18011806find_tabstat_entry (Oid rel_id )
18021807{
18031808 TabStatHashEntry * hash_entry ;
18041809
1805- /*
1806- * There are no entries at all.
1807- */
1810+ /* If hashtable doesn't exist, there are no entries at all */
18081811 if (!pgStatTabHash )
18091812 return NULL ;
18101813
18111814 hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_FIND , NULL );
18121815 if (!hash_entry )
18131816 return NULL ;
18141817
1818+ /* Note that this step could also return NULL, but that's correct */
18151819 return hash_entry -> tsa_entry ;
18161820}
18171821
0 commit comments