*** a/contrib/pg_stat_statements/Makefile --- b/contrib/pg_stat_statements/Makefile *************** *** 5,10 **** OBJS = pg_stat_statements.o --- 5,11 ---- EXTENSION = pg_stat_statements DATA = pg_stat_statements--1.1.sql pg_stat_statements--1.0--1.1.sql \ + pg_stat_statements--1.2.sql pg_stat_statements--1.1--1.2.sql \ pg_stat_statements--unpackaged--1.0.sql ifdef USE_PGXS *** /dev/null --- b/contrib/pg_stat_statements/pg_stat_statements--1.1--1.2.sql *************** *** 0 **** --- 1,44 ---- + /* contrib/pg_stat_statements/pg_stat_statements--1.1--1.2.sql */ + + -- complain if script is sourced in psql, rather than via ALTER EXTENSION + \echo Use "ALTER EXTENSION pg_stat_statements UPDATE" to load this file. \quit + + /* First we have to remove them from the extension */ + ALTER EXTENSION pg_stat_statements DROP VIEW pg_stat_statements; + ALTER EXTENSION pg_stat_statements DROP FUNCTION pg_stat_statements(); + + /* Then we can drop them */ + DROP VIEW pg_stat_statements; + DROP FUNCTION pg_stat_statements(); + + /* Now redefine */ + CREATE FUNCTION pg_stat_statements( + OUT userid oid, + OUT dbid oid, + OUT stat_session_id int8, + OUT query text, + OUT query_id int4, + OUT calls int8, + OUT total_time float8, + OUT rows int8, + OUT shared_blks_hit int8, + OUT shared_blks_read int8, + OUT shared_blks_dirtied int8, + OUT shared_blks_written int8, + OUT local_blks_hit int8, + OUT local_blks_read int8, + OUT local_blks_dirtied int8, + OUT local_blks_written int8, + OUT temp_blks_read int8, + OUT temp_blks_written int8, + OUT blk_read_time float8, + OUT blk_write_time float8 + ) + RETURNS SETOF record + AS 'MODULE_PATHNAME' + LANGUAGE C; + + CREATE VIEW pg_stat_statements AS + SELECT * FROM pg_stat_statements(); + + GRANT SELECT ON pg_stat_statements TO PUBLIC; *** /dev/null --- b/contrib/pg_stat_statements/pg_stat_statements--1.2.sql *************** *** 0 **** --- 1,45 ---- + /* contrib/pg_stat_statements/pg_stat_statements--1.2.sql */ + + -- complain if script is sourced in psql, rather than via CREATE EXTENSION + \echo Use "CREATE EXTENSION pg_stat_statements" to load this file. \quit + + -- Register functions. + CREATE FUNCTION pg_stat_statements_reset() + RETURNS void + AS 'MODULE_PATHNAME' + LANGUAGE C; + + CREATE FUNCTION pg_stat_statements( + OUT userid oid, + OUT dbid oid, + OUT stat_session_id int4, + OUT query text, + OUT query_id int8, + OUT calls int8, + OUT total_time float8, + OUT rows int8, + OUT shared_blks_hit int8, + OUT shared_blks_read int8, + OUT shared_blks_dirtied int8, + OUT shared_blks_written int8, + OUT local_blks_hit int8, + OUT local_blks_read int8, + OUT local_blks_dirtied int8, + OUT local_blks_written int8, + OUT temp_blks_read int8, + OUT temp_blks_written int8, + OUT blk_read_time float8, + OUT blk_write_time float8 + ) + RETURNS SETOF record + AS 'MODULE_PATHNAME' + LANGUAGE C; + + -- Register a view on the function for ease of use. + CREATE VIEW pg_stat_statements AS + SELECT * FROM pg_stat_statements(); + + GRANT SELECT ON pg_stat_statements TO PUBLIC; + + -- Don't want this to be available to non-superusers. + REVOKE ALL ON FUNCTION pg_stat_statements_reset() FROM PUBLIC; *** a/contrib/pg_stat_statements/pg_stat_statements.c --- b/contrib/pg_stat_statements/pg_stat_statements.c *************** *** 43,48 **** --- 43,49 ---- */ #include "postgres.h" + #include #include #include "access/hash.h" *************** *** 67,73 **** PG_MODULE_MAGIC; #define PGSS_DUMP_FILE "global/pg_stat_statements.stat" /* This constant defines the magic number in the stats file header */ ! static const uint32 PGSS_FILE_HEADER = 0x20120328; /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ #define USAGE_EXEC(duration) (1.0) --- 68,74 ---- #define PGSS_DUMP_FILE "global/pg_stat_statements.stat" /* This constant defines the magic number in the stats file header */ ! static const uint32 PGSS_FILE_HEADER = 0x20130820; /* XXX: Should USAGE_EXEC reflect execution time and/or buffer usage? */ #define USAGE_EXEC(duration) (1.0) *************** *** 96,101 **** typedef struct pgssHashKey --- 97,116 ---- } pgssHashKey; /* + * Identifies the tuple format detected by pg_stat_statements. + * + * Used to identify features of newer formats and enable smooth + * upgrades: one can install a new pg_stat_statements binary while + * running with the old SQL function definitions. + */ + typedef enum pgssTupVersion + { + PGSS_TUP_V1_0 = 1, + PGSS_TUP_V1_1, + PGSS_TUP_LATEST + } pgssTupVersion; + + /* * The actual stats counters kept within pgssEntry. */ typedef struct Counters *************** *** 128,133 **** typedef struct pgssEntry --- 143,149 ---- pgssHashKey key; /* hash key of entry - MUST BE FIRST */ Counters counters; /* the statistics for this query */ int query_len; /* # of valid bytes in query string */ + uint32 query_id; /* jumble value for this entry */ slock_t mutex; /* protects the counters only */ char query[1]; /* VARIABLE LENGTH ARRAY - MUST BE LAST */ /* Note: the allocated length of query[] is actually pgss->query_size */ *************** *** 141,146 **** typedef struct pgssSharedState --- 157,171 ---- LWLockId lock; /* protects hashtable search/modification */ int query_size; /* max query length in bytes */ double cur_median_usage; /* current median usage in hashtable */ + + /* + * Is set to a new random value when the server restarts or stats + * are reset, to de-confuse cases of non-monotonic counters (as + * they had reasons to decrease) for tools that aggregate + * snapshots of pg_stat_statements. + */ + int32 stat_session_key; + uint64 private_stat_session_key; } pgssSharedState; /* *************** *** 192,197 **** static ProcessUtility_hook_type prev_ProcessUtility = NULL; --- 217,226 ---- static pgssSharedState *pgss = NULL; static HTAB *pgss_hash = NULL; + /* Random number seeding */ + static unsigned int random_seed = 0; + static struct timeval random_start_time; + /*---- GUC variables ----*/ typedef enum *************** *** 230,235 **** Datum pg_stat_statements(PG_FUNCTION_ARGS); --- 259,266 ---- PG_FUNCTION_INFO_V1(pg_stat_statements_reset); PG_FUNCTION_INFO_V1(pg_stat_statements); + static long pgssRandom(void); + static void pgss_new_stat_session(pgssSharedState *state); static void pgss_shmem_startup(void); static void pgss_shmem_shutdown(int code, Datum arg); static void pgss_post_parse_analyze(ParseState *pstate, Query *query); *************** *** 251,257 **** static void pgss_store(const char *query, uint32 queryId, pgssJumbleState *jstate); static Size pgss_memsize(void); 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, --- 282,288 ---- pgssJumbleState *jstate); static Size pgss_memsize(void); static pgssEntry *entry_alloc(pgssHashKey *key, const char *query, ! int query_len, uint32 query_id, bool sticky); static void entry_dealloc(void); static void entry_reset(void); static void AppendJumble(pgssJumbleState *jstate, *************** *** 291,297 **** _PG_init(void) NULL, &pgss_max, 1000, ! 100, INT_MAX, PGC_POSTMASTER, 0, --- 322,328 ---- NULL, &pgss_max, 1000, ! 1, INT_MAX, PGC_POSTMASTER, 0, *************** *** 379,384 **** _PG_fini(void) --- 410,463 ---- } /* + * Random numbers -- ported from PostmasterRandom. + */ + static long + pgssRandom(void) + { + /* + * Select a random seed if one hasn't been selected before. + */ + if (random_seed == 0) + { + do + { + struct timeval random_stop_time; + + gettimeofday(&random_stop_time, NULL); + + /* + * We are not sure how much precision is in tv_usec, so we swap + * the high and low 16 bits of 'random_stop_time' and XOR them + * with 'random_start_time'. On the off chance that the result is + * 0, we loop until it isn't. + */ + random_seed = random_start_time.tv_usec ^ + ((random_stop_time.tv_usec << 16) | + ((random_stop_time.tv_usec >> 16) & 0xffff)); + } + while (random_seed == 0); + + srandom(random_seed); + } + + return random(); + } + + /* + * Create a new, random stat session key. + */ + static void + pgss_new_stat_session(pgssSharedState *state) + { + state->stat_session_key = (int32) pgssRandom(); + + /* Generate 64-bit private session */ + state->private_stat_session_key = (uint64) pgssRandom(); + state->private_stat_session_key |= ((uint64) pgssRandom()) << 32; + } + + /* * shmem_startup hook: allocate or attach to shared memory, * then load any pre-existing statistics from file. */ *************** *** 394,399 **** pgss_shmem_startup(void) --- 473,479 ---- int query_size; int buffer_size; char *buffer = NULL; + bool log_cannot_read = true; if (prev_shmem_startup_hook) prev_shmem_startup_hook(); *************** *** 456,471 **** pgss_shmem_startup(void) if (file == NULL) { if (errno == ENOENT) ! return; /* ignore not-found error */ goto error; } buffer_size = query_size; buffer = (char *) palloc(buffer_size); if (fread(&header, sizeof(uint32), 1, file) != 1 || ! header != PGSS_FILE_HEADER || ! fread(&num, sizeof(int32), 1, file) != 1) goto error; for (i = 0; i < num; i++) --- 536,566 ---- if (file == NULL) { if (errno == ENOENT) ! log_cannot_read = false; ! goto error; } buffer_size = query_size; buffer = (char *) palloc(buffer_size); + /* Check header existence and magic number match. */ if (fread(&header, sizeof(uint32), 1, file) != 1 || ! header != PGSS_FILE_HEADER) ! goto error; ! ! /* Restore saved session key, if possible. */ ! if (fread(&pgss->stat_session_key, ! sizeof pgss->stat_session_key, 1, file) != 1) ! goto error; ! ! /* Restore private session key */ ! if (fread(&pgss->private_stat_session_key, ! sizeof pgss->private_stat_session_key, 1, file) != 1) ! goto error; ! ! /* Read how many table entries there are. */ ! if (fread(&num, sizeof(int32), 1, file) != 1) goto error; for (i = 0; i < num; i++) *************** *** 503,509 **** pgss_shmem_startup(void) query_size - 1); /* make the hashtable entry (discards old entries if too many) */ ! entry = entry_alloc(&temp.key, buffer, temp.query_len, false); /* copy in the actual stats */ entry->counters = temp.counters; --- 598,605 ---- query_size - 1); /* make the hashtable entry (discards old entries if too many) */ ! entry = entry_alloc(&temp.key, buffer, temp.query_len, temp.query_id, ! false); /* copy in the actual stats */ entry->counters = temp.counters; *************** *** 521,530 **** pgss_shmem_startup(void) return; error: ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not read pg_stat_statement file \"%s\": %m", ! PGSS_DUMP_FILE))); if (buffer) pfree(buffer); if (file) --- 617,632 ---- return; error: ! /* Ignore not-found error. */ ! if (log_cannot_read) ! ereport(LOG, ! (errcode_for_file_access(), ! errmsg("could not read pg_stat_statement file \"%s\": %m", ! PGSS_DUMP_FILE))); ! ! /* Need a new session if none could be read from the file */ ! pgss_new_stat_session(pgss); ! if (buffer) pfree(buffer); if (file) *************** *** 563,570 **** pgss_shmem_shutdown(int code, Datum arg) --- 665,685 ---- if (file == NULL) goto error; + /* Save header/magic number. */ if (fwrite(&PGSS_FILE_HEADER, sizeof(uint32), 1, file) != 1) goto error; + + /* Save stat session key. */ + if (fwrite(&pgss->stat_session_key, + sizeof pgss->stat_session_key, 1, file) != 1) + goto error; + + /* Save private session key */ + if (fwrite(&pgss->private_stat_session_key, + sizeof pgss->private_stat_session_key, 1, file) != 1) + goto error; + + /* Write how many table entries there are. */ num_entries = hash_get_num_entries(pgss_hash); if (fwrite(&num_entries, sizeof(int32), 1, file) != 1) goto error; *************** *** 991,997 **** pgss_store(const char *query, uint32 queryId, /* Acquire exclusive lock as required by entry_alloc() */ LWLockAcquire(pgss->lock, LW_EXCLUSIVE); ! entry = entry_alloc(&key, norm_query, query_len, true); } else { --- 1106,1112 ---- /* Acquire exclusive lock as required by entry_alloc() */ LWLockAcquire(pgss->lock, LW_EXCLUSIVE); ! entry = entry_alloc(&key, norm_query, query_len, queryId, true); } else { *************** *** 1008,1014 **** pgss_store(const char *query, uint32 queryId, /* Acquire exclusive lock as required by entry_alloc() */ LWLockAcquire(pgss->lock, LW_EXCLUSIVE); ! entry = entry_alloc(&key, query, query_len, false); } } --- 1123,1129 ---- /* Acquire exclusive lock as required by entry_alloc() */ LWLockAcquire(pgss->lock, LW_EXCLUSIVE); ! entry = entry_alloc(&key, query, query_len, queryId, false); } } *************** *** 1069,1075 **** pg_stat_statements_reset(PG_FUNCTION_ARGS) } #define PG_STAT_STATEMENTS_COLS_V1_0 14 ! #define PG_STAT_STATEMENTS_COLS 18 /* * Retrieve statement statistics. --- 1184,1191 ---- } #define PG_STAT_STATEMENTS_COLS_V1_0 14 ! #define PG_STAT_STATEMENTS_COLS_V1_1 18 ! #define PG_STAT_STATEMENTS_COLS 20 /* * Retrieve statement statistics. *************** *** 1086,1092 **** pg_stat_statements(PG_FUNCTION_ARGS) bool is_superuser = superuser(); HASH_SEQ_STATUS hash_seq; pgssEntry *entry; ! bool sql_supports_v1_1_counters = true; if (!pgss || !pgss_hash) ereport(ERROR, --- 1202,1208 ---- bool is_superuser = superuser(); HASH_SEQ_STATUS hash_seq; pgssEntry *entry; ! pgssTupVersion detected_version; if (!pgss || !pgss_hash) ereport(ERROR, *************** *** 1107,1114 **** pg_stat_statements(PG_FUNCTION_ARGS) /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); if (tupdesc->natts == PG_STAT_STATEMENTS_COLS_V1_0) ! sql_supports_v1_1_counters = false; per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); --- 1223,1250 ---- /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); + + /* Perform version detection */ if (tupdesc->natts == PG_STAT_STATEMENTS_COLS_V1_0) ! detected_version = PGSS_TUP_V1_0; ! else if (tupdesc->natts == PG_STAT_STATEMENTS_COLS_V1_1) ! detected_version = PGSS_TUP_V1_1; ! else if (tupdesc->natts == PG_STAT_STATEMENTS_COLS) ! detected_version = PGSS_TUP_LATEST; ! else ! { ! /* ! * Couldn't identify the tuple format. Raise error. ! * ! * This is an exceptional case that may only happen in bizarre ! * situations, since it is thought that every released version ! * of pg_stat_statements has a matching schema. ! */ ! ereport(ERROR, ! (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), ! errmsg("pg_stat_statements schema is not supported " ! "by its installed binary"))); ! } per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); *************** *** 1135,1140 **** pg_stat_statements(PG_FUNCTION_ARGS) --- 1271,1277 ---- values[i++] = ObjectIdGetDatum(entry->key.userid); values[i++] = ObjectIdGetDatum(entry->key.dbid); + values[i++] = DatumGetInt32(pgss->stat_session_key); if (is_superuser || entry->key.userid == userid) { *************** *** 1150,1156 **** pg_stat_statements(PG_FUNCTION_ARGS) pfree(qstr); } else ! values[i++] = CStringGetTextDatum(""); /* copy counters to a local variable to keep locking time short */ { --- 1287,1307 ---- pfree(qstr); } else ! { ! /* ! * The role calling this function is unable to see ! * sensitive aspects of this tuple. ! * ! * Nullify everything except the "insufficient privilege" ! * message for this entry ! */ ! memset(nulls, 1, sizeof nulls); ! ! nulls[i] = 0; ! values[i] = CStringGetTextDatum(""); ! ! i += 1; ! } /* copy counters to a local variable to keep locking time short */ { *************** *** 1165,1193 **** pg_stat_statements(PG_FUNCTION_ARGS) if (tmp.calls == 0) continue; values[i++] = Int64GetDatumFast(tmp.calls); values[i++] = Float8GetDatumFast(tmp.total_time); values[i++] = Int64GetDatumFast(tmp.rows); values[i++] = Int64GetDatumFast(tmp.shared_blks_hit); values[i++] = Int64GetDatumFast(tmp.shared_blks_read); ! if (sql_supports_v1_1_counters) values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied); values[i++] = Int64GetDatumFast(tmp.shared_blks_written); values[i++] = Int64GetDatumFast(tmp.local_blks_hit); values[i++] = Int64GetDatumFast(tmp.local_blks_read); ! if (sql_supports_v1_1_counters) values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied); values[i++] = Int64GetDatumFast(tmp.local_blks_written); values[i++] = Int64GetDatumFast(tmp.temp_blks_read); values[i++] = Int64GetDatumFast(tmp.temp_blks_written); ! if (sql_supports_v1_1_counters) { values[i++] = Float8GetDatumFast(tmp.blk_read_time); values[i++] = Float8GetDatumFast(tmp.blk_write_time); } ! Assert(i == (sql_supports_v1_1_counters ? ! PG_STAT_STATEMENTS_COLS : PG_STAT_STATEMENTS_COLS_V1_0)); tuplestore_putvalues(tupstore, tupdesc, values, nulls); } --- 1316,1369 ---- if (tmp.calls == 0) continue; + if (detected_version >= PGSS_TUP_LATEST) + { + uint64 qid = pgss->private_stat_session_key; + + qid ^= (uint64) entry->query_id; + qid ^= ((uint64) entry->query_id) << 32; + + values[i++] = Int64GetDatumFast(qid); + } + values[i++] = Int64GetDatumFast(tmp.calls); values[i++] = Float8GetDatumFast(tmp.total_time); values[i++] = Int64GetDatumFast(tmp.rows); values[i++] = Int64GetDatumFast(tmp.shared_blks_hit); values[i++] = Int64GetDatumFast(tmp.shared_blks_read); ! if (detected_version >= PGSS_TUP_V1_1) values[i++] = Int64GetDatumFast(tmp.shared_blks_dirtied); values[i++] = Int64GetDatumFast(tmp.shared_blks_written); values[i++] = Int64GetDatumFast(tmp.local_blks_hit); values[i++] = Int64GetDatumFast(tmp.local_blks_read); ! if (detected_version >= PGSS_TUP_V1_1) values[i++] = Int64GetDatumFast(tmp.local_blks_dirtied); values[i++] = Int64GetDatumFast(tmp.local_blks_written); values[i++] = Int64GetDatumFast(tmp.temp_blks_read); values[i++] = Int64GetDatumFast(tmp.temp_blks_written); ! if (detected_version >= PGSS_TUP_V1_1) { values[i++] = Float8GetDatumFast(tmp.blk_read_time); values[i++] = Float8GetDatumFast(tmp.blk_write_time); } ! #ifdef USE_ASSERT_CHECKING ! /* Check that every column appears to be filled */ ! switch (detected_version) ! { ! case PGSS_TUP_V1_0: ! Assert(i == PG_STAT_STATEMENTS_COLS_V1_0); ! break; ! case PGSS_TUP_V1_1: ! Assert(i == PG_STAT_STATEMENTS_COLS_V1_1); ! break; ! case PGSS_TUP_LATEST: ! Assert(i == PG_STAT_STATEMENTS_COLS); ! break; ! default: ! Assert(false); ! } ! #endif tuplestore_putvalues(tupstore, tupdesc, values, nulls); } *************** *** 1234,1240 **** pgss_memsize(void) * have made the entry while we waited to get exclusive lock. */ static pgssEntry * ! entry_alloc(pgssHashKey *key, const char *query, int query_len, bool sticky) { pgssEntry *entry; bool found; --- 1410,1417 ---- * have made the entry while we waited to get exclusive lock. */ static pgssEntry * ! entry_alloc(pgssHashKey *key, const char *query, int query_len, ! uint32 query_id, bool sticky) { pgssEntry *entry; bool found; *************** *** 1254,1259 **** entry_alloc(pgssHashKey *key, const char *query, int query_len, bool sticky) --- 1431,1437 ---- memset(&entry->counters, 0, sizeof(Counters)); /* 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 */ *************** *** 1261,1266 **** entry_alloc(pgssHashKey *key, const char *query, int query_len, bool sticky) --- 1439,1447 ---- entry->query_len = query_len; memcpy(entry->query, query, query_len); entry->query[query_len] = '\0'; + + /* Copy in the query id for reporting */ + entry->query_id = query_id; } return entry; *************** *** 1309,1314 **** entry_dealloc(void) --- 1490,1496 ---- while ((entry = hash_seq_search(&hash_seq)) != NULL) { entries[i++] = entry; + /* "Sticky" entries get a different usage decay rate. */ if (entry->counters.calls == 0) entry->counters.usage *= STICKY_DECREASE_FACTOR; *************** *** 1350,1355 **** entry_reset(void) --- 1532,1543 ---- hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL); } + /* + * Counters all reset, so need to generate a new identity for the + * session. + */ + pgss_new_stat_session(pgss); + LWLockRelease(pgss->lock); } *** a/contrib/pg_stat_statements/pg_stat_statements.control --- b/contrib/pg_stat_statements/pg_stat_statements.control *************** *** 1,5 **** # pg_stat_statements extension comment = 'track execution statistics of all SQL statements executed' ! default_version = '1.1' module_pathname = '$libdir/pg_stat_statements' relocatable = true --- 1,5 ---- # pg_stat_statements extension comment = 'track execution statistics of all SQL statements executed' ! default_version = '1.2' module_pathname = '$libdir/pg_stat_statements' relocatable = true *** a/doc/src/sgml/pgstatstatements.sgml --- b/doc/src/sgml/pgstatstatements.sgml *************** *** 57,76 **** OID of database in which the statement was executed ! query text Text of a representative statement (up to bytes) ! calls bigint Number of times executed ! total_time double precision --- 57,87 ---- OID of database in which the statement was executed ! query text Text of a representative statement (up to bytes) ! ! queryid ! bigint ! ! Unique value of each representative statement for the current statistics session. ! This value will change for each new statistics session. ! calls bigint Number of times executed ! ! calls_underest ! bigint ! ! Number indicating if the statement statistics has a probability of being erroneous. ! total_time double precision *************** *** 228,233 **** --- 239,254 ---- might appear as separate entries, if they have different meanings as a result of factors such as different search_path settings. + + + There is a possibility that,for queries which are tracked intermittently,could have their + statistics silently reset while query is not being tracked.The calls_underest + field gives an indication that a query has been tracked intermittently and consequently + has a probability of erroneous statistics. Queries tracked constantly will have a zero value for + calls_underest indicating zero probability of erroneous statistics. + A greater value of calls_underest indicates greater degree of intermittent tracking which in + turn means greater probability of erroneous statistics. +