@@ -72,8 +72,9 @@ static const uint32 PGSS_FILE_HEADER = 0x20120328;
7272/* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */
7373#define USAGE_EXEC (duration )(1.0)
7474#define USAGE_INIT (1.0)/* including initial planning */
75- #define USAGE_NON_EXEC_STICK (3 .0)/*to make new entries sticky */
75+ #define ASSUMED_MEDIAN_INIT (10 .0)/*initial assumed median usage */
7676#define USAGE_DECREASE_FACTOR (0.99)/* decreased every entry_dealloc */
77+ #define STICKY_DECREASE_FACTOR (0.50)/* factor for sticky entries */
7778#define USAGE_DEALLOC_PERCENT 5/* free this % of entries at once */
7879
7980#define JUMBLE_SIZE 1024/* query serialization buffer size */
@@ -139,6 +140,7 @@ typedef struct pgssSharedState
139140{
140141LWLockId lock ;/* protects hashtable search/modification */
141142int query_size ;/* max query length in bytes */
143+ double cur_median_usage ;/* current median usage in hashtable */
142144}pgssSharedState ;
143145
144146/*
@@ -413,6 +415,7 @@ pgss_shmem_startup(void)
413415/* First time through ... */
414416pgss -> lock = LWLockAssign ();
415417pgss -> query_size = pgstat_track_activity_query_size ;
418+ pgss -> cur_median_usage = ASSUMED_MEDIAN_INIT ;
416419}
417420
418421/* Be sure everyone agrees on the hash table entry size */
@@ -908,7 +911,7 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
908911/*
909912 * Given an arbitrarily long query string, produce a hash for the purposes of
910913 * identifying the query, without normalizing constants. Used when hashing
911- * utility statements, or for legacy compatibility mode .
914+ * utility statements.
912915 */
913916static uint32
914917pgss_hash_string (const char * str )
@@ -951,17 +954,28 @@ pgss_store(const char *query, uint32 queryId,
951954
952955entry = (pgssEntry * )hash_search (pgss_hash ,& key ,HASH_FIND ,NULL );
953956
954- /*
955- * When creating an entry just to store the normalized string, make it
956- * artificially sticky so that it will probably still be there when
957- * executed. Strictly speaking, query strings are normalized on a best
958- * effort basis, though it would be difficult to demonstrate this even
959- * under artificial conditions.
960- */
961- if (jstate && !entry )
962- usage = USAGE_NON_EXEC_STICK ;
957+ if (jstate )
958+ {
959+ /*
960+ * When creating an entry just to store the normalized string, make it
961+ * artificially sticky so that it will probably still be there when
962+ * the query gets executed. We do this by giving it a median usage
963+ * value rather than the normal value. (Strictly speaking, query
964+ * strings are normalized on a best effort basis, though it would be
965+ * difficult to demonstrate this even under artificial conditions.)
966+ * But if we found the entry already present, don't let this call
967+ * increment its usage.
968+ */
969+ if (!entry )
970+ usage = pgss -> cur_median_usage ;
971+ else
972+ usage = 0 ;
973+ }
963974else
975+ {
976+ /* normal case, increment usage by normal amount */
964977usage = USAGE_EXEC (duration );
978+ }
965979
966980if (!entry )
967981{
@@ -1185,8 +1199,8 @@ pg_stat_statements(PG_FUNCTION_ARGS)
11851199values [i ++ ]= Float8GetDatumFast (tmp .time_write );
11861200}
11871201
1188- Assert (i == sql_supports_v1_1_counters ?
1189- PG_STAT_STATEMENTS_COLS :PG_STAT_STATEMENTS_COLS_V1_0 );
1202+ Assert (i == ( sql_supports_v1_1_counters ?
1203+ PG_STAT_STATEMENTS_COLS :PG_STAT_STATEMENTS_COLS_V1_0 ) );
11901204
11911205tuplestore_putvalues (tupstore ,tupdesc ,values ,nulls );
11921206}
@@ -1288,7 +1302,11 @@ entry_dealloc(void)
12881302int nvictims ;
12891303int i ;
12901304
1291- /* Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them. */
1305+ /*
1306+ * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them.
1307+ * While we're scanning the table, apply the decay factor to the usage
1308+ * values.
1309+ */
12921310
12931311entries = palloc (hash_get_num_entries (pgss_hash )* sizeof (pgssEntry * ));
12941312
@@ -1297,10 +1315,19 @@ entry_dealloc(void)
12971315while ((entry = hash_seq_search (& hash_seq ))!= NULL )
12981316{
12991317entries [i ++ ]= entry ;
1300- entry -> counters .usage *=USAGE_DECREASE_FACTOR ;
1318+ /* "Sticky" entries get a different usage decay rate. */
1319+ if (entry -> counters .calls == 0 )
1320+ entry -> counters .usage *=STICKY_DECREASE_FACTOR ;
1321+ else
1322+ entry -> counters .usage *=USAGE_DECREASE_FACTOR ;
13011323}
13021324
13031325qsort (entries ,i ,sizeof (pgssEntry * ),entry_cmp );
1326+
1327+ /* Also, record the (approximate) median usage */
1328+ if (i > 0 )
1329+ pgss -> cur_median_usage = entries [i /2 ]-> counters .usage ;
1330+
13041331nvictims = Max (10 ,i * USAGE_DEALLOC_PERCENT /100 );
13051332nvictims = Min (nvictims ,i );
13061333