const BufferUsage *bufusage,
pgssJumbleState * jstate);
static Size pgss_memsize(void);
-static pgssEntry *entry_alloc(pgssHashKey *key, const char *query, int query_len);
+static pgssEntry *entry_alloc(pgssHashKey *key, const char *query,
+ int query_len, bool sticky);
static void entry_dealloc(void);
static void entry_reset(void);
static void AppendJumble(pgssJumbleState * jstate,
query_size - 1);
/* make the hashtable entry (discards old entries if too many) */
- entry = entry_alloc(&temp.key, buffer, temp.query_len);
+ entry = entry_alloc(&temp.key, buffer, temp.query_len, false);
/* copy in the actual stats */
entry->counters = temp.counters;
pgss_post_parse_analyze(ParseState *pstate, Query *query)
{
pgssJumbleState jstate;
- BufferUsage bufusage;
/* Assert we didn't do this already */
Assert(query->queryId == 0);
* there's no need for an early entry.
*/
if (jstate.clocations_count > 0)
- {
- memset(&bufusage, 0, sizeof(bufusage));
-
pgss_store(pstate->p_sourcetext,
query->queryId,
0,
0,
- &bufusage,
+ NULL,
&jstate);
- }
}
/*
*
* If jstate is not NULL then we're trying to create an entry for which
* we have no statistics as yet; we just want to record the normalized
- * query string while we can.
+ * query string. total_time, rows, bufusage are ignored in this case.
*/
static void
pgss_store(const char *query, uint32 queryId,
pgssJumbleState * jstate)
{
pgssHashKey key;
- double usage;
pgssEntry *entry;
char *norm_query = NULL;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- if (jstate)
- {
- /*
- * When creating an entry just to store the normalized string, make it
- * artificially sticky so that it will probably still be there when
- * the query gets executed. We do this by giving it a median usage
- * value rather than the normal value. (Strictly speaking, query
- * strings are normalized on a best effort basis, though it would be
- * difficult to demonstrate this even under artificial conditions.)
- * But if we found the entry already present, don't let this call
- * increment its usage.
- */
- if (!entry)
- usage = pgss->cur_median_usage;
- else
- usage = 0;
- }
- else
- {
- /* normal case, increment usage by normal amount */
- usage = USAGE_EXEC(duration);
- }
-
+ /* Create new entry, if not present */
if (!entry)
{
int query_len;
/* Acquire exclusive lock as required by entry_alloc() */
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
- entry = entry_alloc(&key, norm_query, query_len);
+ entry = entry_alloc(&key, norm_query, query_len, true);
}
else
{
/* Acquire exclusive lock as required by entry_alloc() */
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
- entry = entry_alloc(&key, query, query_len);
+ entry = entry_alloc(&key, query, query_len, false);
}
}
- /*
- * Grab the spinlock while updating the counters (see comment about
- * locking rules at the head of the file)
- */
+ /* Increment the counts, except when jstate is not NULL */
+ if (!jstate)
{
+ /*
+ * Grab the spinlock while updating the counters (see comment about
+ * locking rules at the head of the file)
+ */
volatile pgssEntry *e = (volatile pgssEntry *) entry;
SpinLockAcquire(&e->mutex);
- /*
- * If we're entering real data, "unstick" entry if it was previously
- * sticky, and then increment calls.
- */
- if (!jstate)
- {
- if (e->counters.calls == 0)
- e->counters.usage = USAGE_INIT;
-
- e->counters.calls += 1;
- }
+ /* "Unstick" entry if it was previously sticky */
+ if (e->counters.calls == 0)
+ e->counters.usage = USAGE_INIT;
+ e->counters.calls += 1;
e->counters.total_time += total_time;
e->counters.rows += rows;
e->counters.shared_blks_hit += bufusage->shared_blks_hit;
e->counters.temp_blks_written += bufusage->temp_blks_written;
e->counters.time_read += INSTR_TIME_GET_DOUBLE(bufusage->time_read);
e->counters.time_write += INSTR_TIME_GET_DOUBLE(bufusage->time_write);
- e->counters.usage += usage;
+ e->counters.usage += USAGE_EXEC(duration);
SpinLockRelease(&e->mutex);
}
*
* "query" need not be null-terminated; we rely on query_len instead
*
+ * If "sticky" is true, make the new entry artificially sticky so that it will
+ * probably still be there when the query finishes execution. We do this by
+ * giving it a median usage value rather than the normal value. (Strictly
+ * speaking, query strings are normalized on a best effort basis, though it
+ * would be difficult to demonstrate this even under artificial conditions.)
+ *
* Note: despite needing exclusive lock, it's not an error for the target
* entry to already exist. This is because pgss_store releases and
* reacquires lock after failing to find a match; so someone else could
* have made the entry while we waited to get exclusive lock.
*/
static pgssEntry *
-entry_alloc(pgssHashKey *key, const char *query, int query_len)
+entry_alloc(pgssHashKey *key, const char *query, int query_len, bool sticky)
{
pgssEntry *entry;
bool found;
/* reset the statistics */
memset(&entry->counters, 0, sizeof(Counters));
- entry->counters.usage = USAGE_INIT;
+ /* set the appropriate initial usage count */
+ entry->counters.usage = sticky ? pgss->cur_median_usage : USAGE_INIT;
/* re-initialize the mutex each time ... we assume no one using it */
SpinLockInit(&entry->mutex);
/* ... and don't forget the query text */