5555#include "utils/sortsupport.h"
5656#include "utils/syscache.h"
5757#include "utils/timestamp.h"
58+ #include "utils/typcache.h"
5859
5960/* Per-index data for ANALYZE */
6061typedef struct AnlIndexData
@@ -1902,6 +1903,12 @@ ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull)
19021903 */
19031904#define WIDTH_THRESHOLD 1024
19041905
1906+ /*
1907+ * Build a hash table for distinct/MCV tracking only when the statistics
1908+ * target is large enough to justify the overhead of maintaining it.
1909+ */
1910+ #define ANALYZE_HASH_THRESHOLD 100
1911+
19051912#define swapInt (a ,b ) do {int _tmp; _tmp=a; a=b; b=_tmp;} while(0)
19061913#define swapDatum (a ,b ) do {Datum _tmp; _tmp=a; a=b; b=_tmp;} while(0)
19071914
@@ -1920,7 +1927,29 @@ typedef struct
19201927 int * tupnoLink ;
19211928} CompareScalarsContext ;
19221929
1930+ /* Entries in the simplehash hash table used by compute_distinct_stats */
1931+ typedef struct DistinctHashEntry
1932+ {
1933+ Datum value ; /* the value represented by this entry */
1934+ int index ; /* its index in the relevant track */
1935+ uint32 hash ; /* hash code for the Datum */
1936+ char status ; /* status code used by simplehash.h */
1937+ } DistinctHashEntry ;
1938+
1939+ /* private_data for the simplehash hash table */
1940+ typedef struct DistinctHashContext
1941+ {
1942+ FmgrInfo * eqfunc ; /* the equality operator */
1943+ FmgrInfo * hashfunc ; /* the hash function to use */
1944+ Oid collation ; /* collation to use equality and hash calls */
1945+ } DistinctHashContext ;
1946+
19231947
1948+ /* forward reference */
1949+ typedef struct DistinctHash_hash DistinctHash_hash ;
1950+
1951+ static uint32 distinct_hash_hash (DistinctHash_hash * tab , Datum key );
1952+ static bool distinct_hash_equal (DistinctHash_hash * tab , Datum key0 , Datum key1 );
19241953static void compute_trivial_stats (VacAttrStatsP stats ,
19251954 AnalyzeAttrFetchFunc fetchfunc ,
19261955 int samplerows ,
@@ -1942,6 +1971,53 @@ static int analyze_mcv_list(int *mcv_counts,
19421971 int samplerows ,
19431972 double totalrows );
19441973
1974+ /* Define support routines for compute distinct values hash tables */
1975+ #define SH_PREFIX DistinctHash
1976+ #define SH_ELEMENT_TYPE DistinctHashEntry
1977+ #define SH_KEY_TYPE Datum
1978+ #define SH_KEY value
1979+ #define SH_HASH_KEY (tab , key ) distinct_hash_hash(tab, key)
1980+ #define SH_EQUAL (tab , key0 , key1 ) distinct_hash_equal(tab, key0, key1)
1981+ #define SH_SCOPE static inline
1982+ #define SH_STORE_HASH
1983+ #define SH_GET_HASH (tab , ent ) ((ent)->hash)
1984+ #define SH_DEFINE
1985+ #define SH_DECLARE
1986+ #include "lib/simplehash.h"
1987+
1988+
1989+ /*
1990+ * Support functions for the hash tables used by compute_distinct_stats
1991+ */
1992+ static uint32
1993+ distinct_hash_hash (DistinctHash_hash * tab , Datum key )
1994+ {
1995+ DistinctHashContext * context = (DistinctHashContext * ) tab -> private_data ;
1996+ Datum result ;
1997+
1998+ result = FunctionCall1Coll (context -> hashfunc , context -> collation , key );
1999+ return DatumGetUInt32 (result );
2000+ }
2001+
2002+ static bool
2003+ distinct_hash_equal (DistinctHash_hash * tab , Datum key0 , Datum key1 )
2004+ {
2005+ DistinctHashContext * context = (DistinctHashContext * ) tab -> private_data ;
2006+ Datum result ;
2007+
2008+ result = FunctionCall2Coll (context -> eqfunc , context -> collation , key0 , key1 );
2009+ return DatumGetBool (result );
2010+ }
2011+
2012+ static inline void
2013+ distinct_hash_set_index (DistinctHash_hash * hash , Datum value ,
2014+ uint32 value_hash , int index )
2015+ {
2016+ DistinctHashEntry * entry = DistinctHash_lookup_hash (hash , value , value_hash );
2017+
2018+ Assert (entry != NULL );
2019+ entry -> index = index ;
2020+ }
19452021
19462022/*
19472023 * std_typanalyze -- the default type-specific typanalyze function
@@ -2131,15 +2207,20 @@ compute_distinct_stats(VacAttrStatsP stats,
21312207 bool is_varwidth = (!stats -> attrtype -> typbyval &&
21322208 stats -> attrtype -> typlen < 0 );
21332209 FmgrInfo f_cmpeq ;
2210+ TypeCacheEntry * typentry ;
21342211 typedef struct
21352212 {
21362213 Datum value ;
21372214 int count ;
2215+ uint32 hash ;
21382216 } TrackItem ;
21392217 TrackItem * track ;
21402218 int track_cnt ,
21412219 track_max ;
21422220 int num_mcv = stats -> attstattarget ;
2221+ bool use_hash ;
2222+ DistinctHashContext hash_context ;
2223+ DistinctHash_hash * track_hash = NULL ;
21432224 int firstcount1 = 0 , /* index of first singleton in track[] */
21442225 c1_cursor = 0 ; /* next singleton to evict (FIFO) */
21452226 StdAnalyzeData * mystats = (StdAnalyzeData * ) stats -> extra_data ;
@@ -2154,6 +2235,26 @@ compute_distinct_stats(VacAttrStatsP stats,
21542235 track_cnt = 0 ;
21552236
21562237 fmgr_info (mystats -> eqfunc , & f_cmpeq );
2238+ typentry = lookup_type_cache (stats -> attrtypid ,
2239+ TYPECACHE_HASH_PROC_FINFO | TYPECACHE_EQ_OPR );
2240+
2241+ /*
2242+ * For sufficiently large statistics targets, use a hash table to avoid
2243+ * repeated linear searches of the track[] array, but only when we can use
2244+ * the type's default hash support that matches the equality operator.
2245+ */
2246+ use_hash = (num_mcv >= ANALYZE_HASH_THRESHOLD &&
2247+ mystats -> eqopr == typentry -> eq_opr &&
2248+ OidIsValid (typentry -> hash_proc ));
2249+
2250+ if (use_hash )
2251+ {
2252+ hash_context .eqfunc = & f_cmpeq ;
2253+ hash_context .hashfunc = & typentry -> hash_proc_finfo ;
2254+ hash_context .collation = stats -> attrcollid ;
2255+ track_hash = DistinctHash_create (CurrentMemoryContext , track_max ,
2256+ & hash_context );
2257+ }
21572258
21582259 for (i = 0 ; i < samplerows ; i ++ )
21592260 {
@@ -2162,6 +2263,7 @@ compute_distinct_stats(VacAttrStatsP stats,
21622263 bool match ;
21632264 int match_index ,
21642265 j ;
2266+ uint32 value_hash = 0 ;
21652267
21662268 vacuum_delay_point (true);
21672269
@@ -2208,20 +2310,33 @@ compute_distinct_stats(VacAttrStatsP stats,
22082310 /*
22092311 * See if the value matches anything we're already tracking.
22102312 */
2211- match = false;
2212- firstcount1 = track_cnt ;
2213- for (j = 0 ; j < track_cnt ; j ++ )
2313+ if (use_hash )
22142314 {
2215- if (DatumGetBool (FunctionCall2Coll (& f_cmpeq ,
2216- stats -> attrcollid ,
2217- value , track [j ].value )))
2315+ DistinctHashEntry * entry ;
2316+
2317+ value_hash = distinct_hash_hash (track_hash , value );
2318+ entry = DistinctHash_lookup_hash (track_hash , value , value_hash );
2319+ match = (entry != NULL );
2320+ if (match )
2321+ match_index = entry -> index ;
2322+ }
2323+ else
2324+ {
2325+ match = false;
2326+ firstcount1 = track_cnt ;
2327+ for (j = 0 ; j < track_cnt ; j ++ )
22182328 {
2219- match = true;
2220- match_index = j ;
2221- break ;
2329+ if (DatumGetBool (FunctionCall2Coll (& f_cmpeq ,
2330+ stats -> attrcollid ,
2331+ value , track [j ].value )))
2332+ {
2333+ match = true;
2334+ match_index = j ;
2335+ break ;
2336+ }
2337+ if (j < firstcount1 && track [j ].count == 1 )
2338+ firstcount1 = j ;
22222339 }
2223- if (j < firstcount1 && track [j ].count == 1 )
2224- firstcount1 = j ;
22252340 }
22262341
22272342 if (match )
@@ -2236,6 +2351,18 @@ compute_distinct_stats(VacAttrStatsP stats,
22362351 {
22372352 swapDatum (track [j ].value , track [j - 1 ].value );
22382353 swapInt (track [j ].count , track [j - 1 ].count );
2354+ if (use_hash )
2355+ {
2356+ uint32 tmp ;
2357+
2358+ tmp = track [j ].hash ;
2359+ track [j ].hash = track [j - 1 ].hash ;
2360+ track [j - 1 ].hash = tmp ;
2361+ distinct_hash_set_index (track_hash , track [j ].value ,
2362+ track [j ].hash , j );
2363+ distinct_hash_set_index (track_hash , track [j - 1 ].value ,
2364+ track [j - 1 ].hash , j - 1 );
2365+ }
22392366 }
22402367
22412368 /*
@@ -2283,12 +2410,34 @@ compute_distinct_stats(VacAttrStatsP stats,
22832410 insert_index = c1_cursor ++ ;
22842411 if (c1_cursor >= track_cnt )
22852412 c1_cursor = firstcount1 ;
2413+
2414+ if (use_hash )
2415+ {
2416+ DistinctHashEntry * delentry ;
2417+
2418+ delentry = DistinctHash_lookup_hash (track_hash ,
2419+ track [insert_index ].value ,
2420+ track [insert_index ].hash );
2421+ Assert (delentry != NULL );
2422+ DistinctHash_delete_item (track_hash , delentry );
2423+ }
22862424 }
22872425 else
22882426 continue ;
22892427
22902428 track [insert_index ].value = value ;
22912429 track [insert_index ].count = 1 ;
2430+ if (use_hash )
2431+ {
2432+ bool found_hash ;
2433+ DistinctHashEntry * entry ;
2434+
2435+ track [insert_index ].hash = value_hash ;
2436+ entry = DistinctHash_insert_hash (track_hash , value , value_hash ,
2437+ & found_hash );
2438+ Assert (!found_hash );
2439+ entry -> index = insert_index ;
2440+ }
22922441 }
22932442 }
22942443
0 commit comments