@@ -174,7 +174,7 @@ typedef struct TabStatusArray
174
174
static TabStatusArray * pgStatTabList = NULL ;
175
175
176
176
/*
177
- * pgStatTabHash entry
177
+ * pgStatTabHash entry: map from relation OID to PgStat_TableStatus pointer
178
178
*/
179
179
typedef struct TabStatHashEntry
180
180
{
@@ -805,6 +805,17 @@ pgstat_report_stat(bool force)
805
805
return ;
806
806
last_report = now ;
807
807
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
+
808
819
/*
809
820
* Scan through the TabStatusArray struct(s) to find tables that actually
810
821
* have counts, and build messages to send. We have to separate shared
@@ -855,14 +866,6 @@ pgstat_report_stat(bool force)
855
866
tsa -> tsa_used = 0 ;
856
867
}
857
868
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
-
866
869
/*
867
870
* Send partial messages. Make sure that any pending xact commit/abort
868
871
* gets counted, even if there are no table stats to send.
@@ -1707,41 +1710,6 @@ pgstat_initstats(Relation rel)
1707
1710
rel -> pgstat_info = get_tabstat_entry (rel_id , rel -> rd_rel -> relisshared );
1708
1711
}
1709
1712
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
-
1745
1713
/*
1746
1714
* get_tabstat_entry - find or create a PgStat_TableStatus entry for rel
1747
1715
*/
@@ -1753,65 +1721,101 @@ get_tabstat_entry(Oid rel_id, bool isshared)
1753
1721
TabStatusArray * tsa ;
1754
1722
bool found ;
1755
1723
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
+ }
1757
1740
1758
1741
/*
1759
1742
* Find an entry or create a new one.
1760
1743
*/
1761
1744
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 )
1763
1755
return hash_entry -> tsa_entry ;
1764
1756
1765
1757
/*
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.
1768
1761
*/
1769
- tsa = pgStatTabList ;
1770
- while (tsa -> tsa_used == TABSTAT_QUANTUM )
1762
+ if (pgStatTabList == NULL )
1771
1763
{
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
+ }
1777
1769
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 ));
1778
1777
tsa = tsa -> tsa_next ;
1779
1778
}
1780
1779
1781
1780
/*
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.
1783
1783
*/
1784
1784
entry = & tsa -> tsa_entries [tsa -> tsa_used ++ ];
1785
1785
entry -> t_id = rel_id ;
1786
1786
entry -> t_shared = isshared ;
1787
1787
1788
1788
/*
1789
- * Add a corresponding entry to pgStatTabHash.
1789
+ * Now we can fill the entry in pgStatTabHash.
1790
1790
*/
1791
1791
hash_entry -> tsa_entry = entry ;
1792
+
1792
1793
return entry ;
1793
1794
}
1794
1795
1795
1796
/*
1796
1797
* find_tabstat_entry - find any existing PgStat_TableStatus entry for rel
1797
1798
*
1798
1799
* 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".
1799
1804
*/
1800
1805
PgStat_TableStatus *
1801
1806
find_tabstat_entry (Oid rel_id )
1802
1807
{
1803
1808
TabStatHashEntry * hash_entry ;
1804
1809
1805
- /*
1806
- * There are no entries at all.
1807
- */
1810
+ /* If hashtable doesn't exist, there are no entries at all */
1808
1811
if (!pgStatTabHash )
1809
1812
return NULL ;
1810
1813
1811
1814
hash_entry = hash_search (pgStatTabHash , & rel_id , HASH_FIND , NULL );
1812
1815
if (!hash_entry )
1813
1816
return NULL ;
1814
1817
1818
+ /* Note that this step could also return NULL, but that's correct */
1815
1819
return hash_entry -> tsa_entry ;
1816
1820
}
1817
1821
0 commit comments