pg_stat_statements-identification-v6.patch
application/octet-stream
Filename: pg_stat_statements-identification-v6.patch
Type: application/octet-stream
Part: 0
Patch
Format: context
Series: patch v6
| File | + | − |
|---|---|---|
| contrib/pg_stat_statements/Makefile | 1 | 0 |
| contrib/pg_stat_statements/pg_stat_statements--1.1--1.2.sql | 44 | 0 |
| contrib/pg_stat_statements/pg_stat_statements--1.2.sql | 45 | 0 |
| contrib/pg_stat_statements/pg_stat_statements.c | 211 | 0 |
| contrib/pg_stat_statements/pg_stat_statements.control | 1 | 1 |
| doc/src/sgml/pgstatstatements.sgml | 8 | 3 |
*** 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 <time.h>
#include <unistd.h>
#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("<insufficient privilege>");
/* 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("<insufficient privilege>");
!
! 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 ****
<entry>OID of database in which the statement was executed</entry>
</row>
! <row>
<entry><structfield>query</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>Text of a representative statement (up to <xref linkend="guc-track-activity-query-size"> bytes)</entry>
</row>
!
<row>
<entry><structfield>calls</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Number of times executed</entry>
</row>
-
<row>
<entry><structfield>total_time</structfield></entry>
<entry><type>double precision</type></entry>
--- 57,81 ----
<entry>OID of database in which the statement was executed</entry>
</row>
! <row>
<entry><structfield>query</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>Text of a representative statement (up to <xref linkend="guc-track-activity-query-size"> bytes)</entry>
</row>
! <row>
! <entry><structfield>queryid</structfield></entry>
! <entry><type>bigint</type></entry>
! <entry></entry>
! <entry>Unique value of each representative statement for the current statistics session.
! This value will change for each new statistics session.</entry>
! </row>
<row>
<entry><structfield>calls</structfield></entry>
<entry><type>bigint</type></entry>
<entry></entry>
<entry>Number of times executed</entry>
</row>
<row>
<entry><structfield>total_time</structfield></entry>
<entry><type>double precision</type></entry>