Track statement entry timestamp in contrib/pg_stat_statements

This patch adds 'stats_since' and 'minmax_stats_since' columns to the
pg_stat_statements view and pg_stat_statements() function.  The new min/max
reset mode for the pg_stat_stetments_reset() function is controlled by the
parameter minmax_only.

'stat_since' column is populated with the current timestamp when a new
statement is added to the pg_stat_statements hashtable.  It provides clean
information about statistics collection time intervals for each statement.
Besides it can be used by sampling solutions to detect situations when a
statement was evicted and stored again between samples.

Such a sampling solution could derive any pg_stat_statements statistic values
for an interval between two samples with the exception of all min/max
statistics. To address this issue this patch adds the ability to reset
min/max statistics independently of the statement reset using the new
minmax_only parameter of the pg_stat_statements_reset(userid oid, dbid oid,
queryid bigint, minmax_only boolean) function. The timestamp of such reset
is stored in the minmax_stats_since field for each statement.
pg_stat_statements_reset() function now returns the timestamp of a reset as the
result.

Discussion: https://postgr.es/m/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
Author: Andrei Zubkov
Reviewed-by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun
Reviewed-by: Anton Melnikov, Darren Rush, Michael Paquier, Sergei Kornilov
Reviewed-by: Alena Rybakina, Andrei Lepikhov
This commit is contained in:
Alexander Korotkov 2023-11-27 02:51:18 +02:00
parent 6ab1dbd26b
commit dc9f8a7983
9 changed files with 511 additions and 96 deletions

View File

@ -19,7 +19,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
REGRESS = select dml cursors utility level_tracking planning \
user_activity wal cleanup oldextversions
user_activity wal entry_timestamp cleanup oldextversions
# Disabled because these tests require "shared_preload_libraries=pg_stat_statements",
# which typical installcheck users do not have (e.g. buildfarm clients).
NO_INSTALLCHECK = 1

View File

@ -0,0 +1,159 @@
--
-- statement timestamps
--
-- planning time is needed during tests
SET pg_stat_statements.track_planning = TRUE;
SELECT 1 AS "STMTTS1";
STMTTS1
---------
1
(1 row)
SELECT now() AS ref_ts \gset
SELECT 1,2 AS "STMTTS2";
?column? | STMTTS2
----------+---------
1 | 2
(1 row)
SELECT stats_since >= :'ref_ts', count(*) FROM pg_stat_statements
WHERE query LIKE '%STMTTS%'
GROUP BY stats_since >= :'ref_ts'
ORDER BY stats_since >= :'ref_ts';
?column? | count
----------+-------
f | 1
t | 1
(2 rows)
SELECT now() AS ref_ts \gset
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_stats_since_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
total | minmax_plan_zero | minmax_exec_zero | minmax_stats_since_after_ref | stats_since_after_ref
-------+------------------+------------------+------------------------------+-----------------------
2 | 0 | 0 | 0 | 0
(1 row)
-- Perform single min/max reset
SELECT pg_stat_statements_reset(0, 0, queryid, true) AS minmax_reset_ts
FROM pg_stat_statements
WHERE query LIKE '%STMTTS1%' \gset
-- check
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_stats_since_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
total | minmax_plan_zero | minmax_exec_zero | minmax_stats_since_after_ref | stats_since_after_ref
-------+------------------+------------------+------------------------------+-----------------------
2 | 1 | 1 | 1 | 0
(1 row)
-- check minmax reset timestamps
SELECT
query, minmax_stats_since = :'minmax_reset_ts' AS reset_ts_match
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%'
ORDER BY query COLLATE "C";
query | reset_ts_match
---------------------------+----------------
SELECT $1 AS "STMTTS1" | t
SELECT $1,$2 AS "STMTTS2" | f
(2 rows)
-- check that minmax reset does not set stats_reset
SELECT
stats_reset = :'minmax_reset_ts' AS stats_reset_ts_match
FROM pg_stat_statements_info;
stats_reset_ts_match
----------------------
f
(1 row)
-- Perform common min/max reset
SELECT pg_stat_statements_reset(0, 0, 0, true) AS minmax_reset_ts \gset
-- check again
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_ts_after_ref,
count(*) FILTER (
WHERE minmax_stats_since = :'minmax_reset_ts'
) as minmax_ts_match,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
total | minmax_plan_zero | minmax_exec_zero | minmax_ts_after_ref | minmax_ts_match | stats_since_after_ref
-------+------------------+------------------+---------------------+-----------------+-----------------------
2 | 2 | 2 | 2 | 2 | 0
(1 row)
-- Execute first query once more to check stats update
SELECT 1 AS "STMTTS1";
STMTTS1
---------
1
(1 row)
-- check
-- we don't check planing times here to be independent of
-- plan caching approach
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_ts_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
total | minmax_exec_zero | minmax_ts_after_ref | stats_since_after_ref
-------+------------------+---------------------+-----------------------
2 | 1 | 2 | 0
(1 row)
-- Cleanup
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
t
---
t
(1 row)

View File

@ -250,59 +250,61 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
-- New views for pg_stat_statements in 1.11
-- New functions and views for pg_stat_statements in 1.11
AlTER EXTENSION pg_stat_statements UPDATE TO '1.11';
\d pg_stat_statements
View "public.pg_stat_statements"
Column | Type | Collation | Nullable | Default
------------------------+------------------+-----------+----------+---------
userid | oid | | |
dbid | oid | | |
toplevel | boolean | | |
queryid | bigint | | |
query | text | | |
plans | bigint | | |
total_plan_time | double precision | | |
min_plan_time | double precision | | |
max_plan_time | double precision | | |
mean_plan_time | double precision | | |
stddev_plan_time | double precision | | |
calls | bigint | | |
total_exec_time | double precision | | |
min_exec_time | double precision | | |
max_exec_time | double precision | | |
mean_exec_time | double precision | | |
stddev_exec_time | double precision | | |
rows | bigint | | |
shared_blks_hit | bigint | | |
shared_blks_read | bigint | | |
shared_blks_dirtied | bigint | | |
shared_blks_written | bigint | | |
local_blks_hit | bigint | | |
local_blks_read | bigint | | |
local_blks_dirtied | bigint | | |
local_blks_written | bigint | | |
temp_blks_read | bigint | | |
temp_blks_written | bigint | | |
shared_blk_read_time | double precision | | |
shared_blk_write_time | double precision | | |
local_blk_read_time | double precision | | |
local_blk_write_time | double precision | | |
temp_blk_read_time | double precision | | |
temp_blk_write_time | double precision | | |
wal_records | bigint | | |
wal_fpi | bigint | | |
wal_bytes | numeric | | |
jit_functions | bigint | | |
jit_generation_time | double precision | | |
jit_inlining_count | bigint | | |
jit_inlining_time | double precision | | |
jit_optimization_count | bigint | | |
jit_optimization_time | double precision | | |
jit_emission_count | bigint | | |
jit_emission_time | double precision | | |
jit_deform_count | bigint | | |
jit_deform_time | double precision | | |
View "public.pg_stat_statements"
Column | Type | Collation | Nullable | Default
------------------------+--------------------------+-----------+----------+---------
userid | oid | | |
dbid | oid | | |
toplevel | boolean | | |
queryid | bigint | | |
query | text | | |
plans | bigint | | |
total_plan_time | double precision | | |
min_plan_time | double precision | | |
max_plan_time | double precision | | |
mean_plan_time | double precision | | |
stddev_plan_time | double precision | | |
calls | bigint | | |
total_exec_time | double precision | | |
min_exec_time | double precision | | |
max_exec_time | double precision | | |
mean_exec_time | double precision | | |
stddev_exec_time | double precision | | |
rows | bigint | | |
shared_blks_hit | bigint | | |
shared_blks_read | bigint | | |
shared_blks_dirtied | bigint | | |
shared_blks_written | bigint | | |
local_blks_hit | bigint | | |
local_blks_read | bigint | | |
local_blks_dirtied | bigint | | |
local_blks_written | bigint | | |
temp_blks_read | bigint | | |
temp_blks_written | bigint | | |
shared_blk_read_time | double precision | | |
shared_blk_write_time | double precision | | |
local_blk_read_time | double precision | | |
local_blk_write_time | double precision | | |
temp_blk_read_time | double precision | | |
temp_blk_write_time | double precision | | |
wal_records | bigint | | |
wal_fpi | bigint | | |
wal_bytes | numeric | | |
jit_functions | bigint | | |
jit_generation_time | double precision | | |
jit_inlining_count | bigint | | |
jit_inlining_time | double precision | | |
jit_optimization_count | bigint | | |
jit_optimization_time | double precision | | |
jit_emission_count | bigint | | |
jit_emission_time | double precision | | |
jit_deform_count | bigint | | |
jit_deform_time | double precision | | |
stats_since | timestamp with time zone | | |
minmax_stats_since | timestamp with time zone | | |
SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
has_data
@ -310,4 +312,16 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
-- New parameter minmax_only of pg_stat_statements_reset function
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
pg_get_functiondef
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE OR REPLACE FUNCTION public.pg_stat_statements_reset(userid oid DEFAULT 0, dbid oid DEFAULT 0, queryid bigint DEFAULT 0, minmax_only boolean DEFAULT false)+
RETURNS timestamp with time zone +
LANGUAGE c +
PARALLEL SAFE STRICT +
AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_11$function$ +
(1 row)
DROP EXTENSION pg_stat_statements;

View File

@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
'entry_timestamp',
'cleanup',
'oldextversions',
],

View File

@ -3,13 +3,10 @@
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.11'" 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(boolean);
/* Then we can drop them */
/* Drop old versions */
DROP VIEW pg_stat_statements;
DROP FUNCTION pg_stat_statements(boolean);
DROP FUNCTION pg_stat_statements_reset(Oid, Oid, bigint);
/* Now redefine */
CREATE FUNCTION pg_stat_statements(IN showtext boolean,
@ -59,7 +56,9 @@ CREATE FUNCTION pg_stat_statements(IN showtext boolean,
OUT jit_emission_count int8,
OUT jit_emission_time float8,
OUT jit_deform_count int8,
OUT jit_deform_time float8
OUT jit_deform_time float8,
OUT stats_since timestamp with time zone,
OUT minmax_stats_since timestamp with time zone
)
RETURNS SETOF record
AS 'MODULE_PATHNAME', 'pg_stat_statements_1_11'
@ -69,3 +68,15 @@ CREATE VIEW pg_stat_statements AS
SELECT * FROM pg_stat_statements(true);
GRANT SELECT ON pg_stat_statements TO PUBLIC;
CREATE FUNCTION pg_stat_statements_reset(IN userid Oid DEFAULT 0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0,
IN minmax_only boolean DEFAULT false
)
RETURNS timestamp with time zone
AS 'MODULE_PATHNAME', 'pg_stat_statements_reset_1_11'
LANGUAGE C STRICT PARALLEL SAFE;
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_statements_reset(Oid, Oid, bigint, boolean) FROM PUBLIC;

View File

@ -155,9 +155,9 @@ typedef struct Counters
double total_time[PGSS_NUMKIND]; /* total planning/execution time,
* in msec */
double min_time[PGSS_NUMKIND]; /* minimum planning/execution time in
* msec */
* msec since min/max reset */
double max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
* msec */
* msec since min/max reset */
double mean_time[PGSS_NUMKIND]; /* mean planning/execution time in
* msec */
double sum_var_time[PGSS_NUMKIND]; /* sum of variances in
@ -228,6 +228,8 @@ typedef struct pgssEntry
Size query_offset; /* query text offset in external file */
int query_len; /* # of valid bytes in query string, or -1 */
int encoding; /* query text encoding */
TimestampTz stats_since; /* timestamp of entry allocation */
TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@ -308,6 +310,7 @@ static bool pgss_save = true; /* whether to save stats across shutdown */
PG_FUNCTION_INFO_V1(pg_stat_statements_reset);
PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_2);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_3);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_8);
@ -359,7 +362,7 @@ static char *qtext_fetch(Size query_offset, int query_len,
char *buffer, Size buffer_size);
static bool need_gc_qtexts(void);
static void gc_qtexts(void);
static void entry_reset(Oid userid, Oid dbid, uint64 queryid);
static TimestampTz entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only);
static char *generate_normalized_query(JumbleState *jstate, const char *query,
int query_loc, int *query_len_p);
static void fill_in_constant_lengths(JumbleState *jstate, const char *query,
@ -654,6 +657,8 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
entry->stats_since = temp.stats_since;
entry->minmax_stats_since = temp.minmax_stats_since;
}
/* Read global statistics for pg_stat_statements */
@ -1416,11 +1421,23 @@ pgss_store(const char *query, uint64 queryId,
e->counters.sum_var_time[kind] +=
(total_time - old_mean) * (total_time - e->counters.mean_time[kind]);
/* calculate min and max time */
if (e->counters.min_time[kind] > total_time)
/*
* Calculate min and max time. min = 0 and max = 0 means that the
* min/max statistics were reset
*/
if (e->counters.min_time[kind] == 0
&& e->counters.max_time[kind] == 0)
{
e->counters.min_time[kind] = total_time;
if (e->counters.max_time[kind] < total_time)
e->counters.max_time[kind] = total_time;
}
else
{
if (e->counters.min_time[kind] > total_time)
e->counters.min_time[kind] = total_time;
if (e->counters.max_time[kind] < total_time)
e->counters.max_time[kind] = total_time;
}
}
e->counters.rows += rows;
e->counters.shared_blks_hit += bufusage->shared_blks_hit;
@ -1490,18 +1507,34 @@ pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
dbid = PG_GETARG_OID(1);
queryid = (uint64) PG_GETARG_INT64(2);
entry_reset(userid, dbid, queryid);
entry_reset(userid, dbid, queryid, false);
PG_RETURN_VOID();
}
Datum
pg_stat_statements_reset_1_11(PG_FUNCTION_ARGS)
{
Oid userid;
Oid dbid;
uint64 queryid;
bool minmax_only;
userid = PG_GETARG_OID(0);
dbid = PG_GETARG_OID(1);
queryid = (uint64) PG_GETARG_INT64(2);
minmax_only = PG_GETARG_BOOL(3);
PG_RETURN_TIMESTAMPTZ(entry_reset(userid, dbid, queryid, minmax_only));
}
/*
* Reset statement statistics.
*/
Datum
pg_stat_statements_reset(PG_FUNCTION_ARGS)
{
entry_reset(0, 0, 0);
entry_reset(0, 0, 0, false);
PG_RETURN_VOID();
}
@ -1514,8 +1547,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
#define PG_STAT_STATEMENTS_COLS_V1_10 43
#define PG_STAT_STATEMENTS_COLS_V1_11 47
#define PG_STAT_STATEMENTS_COLS 47 /* maximum of above */
#define PG_STAT_STATEMENTS_COLS_V1_11 49
#define PG_STAT_STATEMENTS_COLS 49 /* maximum of above */
/*
* Retrieve statement statistics.
@ -1748,6 +1781,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
TimestampTz stats_since;
TimestampTz minmax_stats_since;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@ -1816,6 +1851,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
stats_since = e->stats_since;
minmax_stats_since = e->minmax_stats_since;
SpinLockRelease(&e->mutex);
}
@ -1912,6 +1949,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
{
values[i++] = Int64GetDatumFast(tmp.jit_deform_count);
values[i++] = Float8GetDatumFast(tmp.jit_deform_time);
values[i++] = TimestampTzGetDatum(stats_since);
values[i++] = TimestampTzGetDatum(minmax_stats_since);
}
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
@ -2030,6 +2069,8 @@ entry_alloc(pgssHashKey *key, Size query_offset, int query_len, int encoding,
entry->query_offset = query_offset;
entry->query_len = query_len;
entry->encoding = encoding;
entry->stats_since = GetCurrentTimestamp();
entry->minmax_stats_since = entry->stats_since;
}
return entry;
@ -2593,11 +2634,30 @@ gc_fail:
record_gc_qtexts();
}
#define SINGLE_ENTRY_RESET(e) \
if (e) { \
if (minmax_only) { \
/* When requested reset only min/max statistics of an entry */ \
for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
{ \
e->counters.max_time[kind] = 0; \
e->counters.min_time[kind] = 0; \
} \
e->minmax_stats_since = stats_reset; \
} \
else \
{ \
/* Remove the key otherwise */ \
hash_search(pgss_hash, &e->key, HASH_REMOVE, NULL); \
num_remove++; \
} \
}
/*
* Release entries corresponding to parameters passed.
* Reset entries corresponding to parameters passed.
*/
static void
entry_reset(Oid userid, Oid dbid, uint64 queryid)
static TimestampTz
entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
@ -2605,6 +2665,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
long num_entries;
long num_remove = 0;
pgssHashKey key;
TimestampTz stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
@ -2614,6 +2675,8 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
num_entries = hash_get_num_entries(pgss_hash);
stats_reset = GetCurrentTimestamp();
if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
{
/* If all the parameters are available, use the fast path. */
@ -2623,22 +2686,23 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.queryid = queryid;
/*
* Remove the key if it exists, starting with the non-top-level entry.
* Reset the entry if it exists, starting with the non-top-level
* entry.
*/
key.toplevel = false;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
if (entry) /* found */
num_remove++;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
/* Also remove the top-level entry if it exists. */
SINGLE_ENTRY_RESET(entry);
/* Also reset the top-level entry if it exists. */
key.toplevel = true;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
if (entry) /* found */
num_remove++;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
SINGLE_ENTRY_RESET(entry);
}
else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
{
/* Remove entries corresponding to valid parameters. */
/* Reset entries corresponding to valid parameters. */
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
@ -2646,19 +2710,17 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
(!dbid || entry->key.dbid == dbid) &&
(!queryid || entry->key.queryid == queryid))
{
hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
num_remove++;
SINGLE_ENTRY_RESET(entry);
}
}
}
else
{
/* Remove all entries. */
/* Reset all entries. */
hash_seq_init(&hash_seq, pgss_hash);
while ((entry = hash_seq_search(&hash_seq)) != NULL)
{
hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
num_remove++;
SINGLE_ENTRY_RESET(entry);
}
}
@ -2672,7 +2734,6 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
*/
{
volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
TimestampTz stats_reset = GetCurrentTimestamp();
SpinLockAcquire(&s->mutex);
s->stats.dealloc = 0;
@ -2710,6 +2771,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
return stats_reset;
}
/*

View File

@ -0,0 +1,114 @@
--
-- statement timestamps
--
-- planning time is needed during tests
SET pg_stat_statements.track_planning = TRUE;
SELECT 1 AS "STMTTS1";
SELECT now() AS ref_ts \gset
SELECT 1,2 AS "STMTTS2";
SELECT stats_since >= :'ref_ts', count(*) FROM pg_stat_statements
WHERE query LIKE '%STMTTS%'
GROUP BY stats_since >= :'ref_ts'
ORDER BY stats_since >= :'ref_ts';
SELECT now() AS ref_ts \gset
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_stats_since_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
-- Perform single min/max reset
SELECT pg_stat_statements_reset(0, 0, queryid, true) AS minmax_reset_ts
FROM pg_stat_statements
WHERE query LIKE '%STMTTS1%' \gset
-- check
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_stats_since_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
-- check minmax reset timestamps
SELECT
query, minmax_stats_since = :'minmax_reset_ts' AS reset_ts_match
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%'
ORDER BY query COLLATE "C";
-- check that minmax reset does not set stats_reset
SELECT
stats_reset = :'minmax_reset_ts' AS stats_reset_ts_match
FROM pg_stat_statements_info;
-- Perform common min/max reset
SELECT pg_stat_statements_reset(0, 0, 0, true) AS minmax_reset_ts \gset
-- check again
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_plan_time + max_plan_time = 0
) as minmax_plan_zero,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_ts_after_ref,
count(*) FILTER (
WHERE minmax_stats_since = :'minmax_reset_ts'
) as minmax_ts_match,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
-- Execute first query once more to check stats update
SELECT 1 AS "STMTTS1";
-- check
-- we don't check planing times here to be independent of
-- plan caching approach
SELECT
count(*) as total,
count(*) FILTER (
WHERE min_exec_time + max_exec_time = 0
) as minmax_exec_zero,
count(*) FILTER (
WHERE minmax_stats_since >= :'ref_ts'
) as minmax_ts_after_ref,
count(*) FILTER (
WHERE stats_since >= :'ref_ts'
) as stats_since_after_ref
FROM pg_stat_statements
WHERE query LIKE '%STMTTS%';
-- Cleanup
SELECT pg_stat_statements_reset() IS NOT NULL AS t;

View File

@ -48,9 +48,11 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.10';
\d pg_stat_statements
SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
-- New views for pg_stat_statements in 1.11
-- New functions and views for pg_stat_statements in 1.11
AlTER EXTENSION pg_stat_statements UPDATE TO '1.11';
\d pg_stat_statements
SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
-- New parameter minmax_only of pg_stat_statements_reset function
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
DROP EXTENSION pg_stat_statements;

View File

@ -140,9 +140,12 @@
<structfield>min_plan_time</structfield> <type>double precision</type>
</para>
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
otherwise zero)
Minimum time spent planning the statement, in milliseconds.
This field will be zero if <varname>pg_stat_statements.track_planning</varname>
is disabled, or if the counter has been reset using the
<function>pg_stat_statements_reset</function> function with the
<structfield>minmax_only</structfield> parameter set to <literal>true</literal>
and never been planned since.
</para></entry>
</row>
@ -151,9 +154,12 @@
<structfield>max_plan_time</structfield> <type>double precision</type>
</para>
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
otherwise zero)
Maximum time spent planning the statement, in milliseconds.
This field will be zero if <varname>pg_stat_statements.track_planning</varname>
is disabled, or if the counter has been reset using the
<function>pg_stat_statements_reset</function> function with the
<structfield>minmax_only</structfield> parameter set to <literal>true</literal>
and never been planned since.
</para></entry>
</row>
@ -203,7 +209,11 @@
<structfield>min_exec_time</structfield> <type>double precision</type>
</para>
<para>
Minimum time spent executing the statement, in milliseconds
Minimum time spent executing the statement, in milliseconds,
this field will be zero until this statement
is executed first time after reset performed by the
<function>pg_stat_statements_reset</function> function with the
<structfield>minmax_only</structfield> parameter set to <literal>true</literal>
</para></entry>
</row>
@ -212,7 +222,11 @@
<structfield>max_exec_time</structfield> <type>double precision</type>
</para>
<para>
Maximum time spent executing the statement, in milliseconds
Maximum time spent executing the statement, in milliseconds,
this field will be zero until this statement
is executed first time after reset performed by the
<function>pg_stat_statements_reset</function> function with the
<structfield>minmax_only</structfield> parameter set to <literal>true</literal>
</para></entry>
</row>
@ -512,6 +526,28 @@
functions, in milliseconds
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>stats_since</structfield> <type>timestamp with time zone</type>
</para>
<para>
Time at which statistics gathering started for this statement
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>minmax_stats_since</structfield> <type>timestamp with time zone</type>
</para>
<para>
Time at which min/max statistics gathering started for this
statement (fields <structfield>min_plan_time</structfield>,
<structfield>max_plan_time</structfield>,
<structfield>min_exec_time</structfield> and
<structfield>max_exec_time</structfield>)
</para></entry>
</row>
</tbody>
</tgroup>
</table>
@ -713,7 +749,8 @@
<variablelist>
<varlistentry>
<term>
<function>pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void</function>
<function>pg_stat_statements_reset(userid Oid, dbid Oid, queryid
bigint, minmax_only boolean) returns timestamp with time zone</function>
<indexterm>
<primary>pg_stat_statements_reset</primary>
</indexterm>
@ -732,6 +769,20 @@
If all statistics in the <filename>pg_stat_statements</filename>
view are discarded, it will also reset the statistics in the
<structname>pg_stat_statements_info</structname> view.
When <structfield>minmax_only</structfield> is <literal>true</literal> only the
values of minimun and maximum planning and execution time will be reset (i.e.
<structfield>min_plan_time</structfield>, <structfield>max_plan_time</structfield>,
<structfield>min_exec_time</structfield> and <structfield>max_exec_time</structfield>
fields). The default value for <structfield>minmax_only</structfield> parameter is
<literal>false</literal>. Time of last min/max reset performed is shown in
<structfield>minmax_stats_since</structfield> field of the
<structname>pg_stat_statements</structname> view.
This function returns the time of a reset. This time is saved to
<structfield>stats_reset</structfield> field of
<structname>pg_stat_statements_info</structname> view or to
<structfield>minmax_stats_since</structfield> field of the
<structname>pg_stat_statements</structname> view if the corresponding reset was
actually performed.
By default, this function can only be executed by superusers.
Access may be granted to others using <command>GRANT</command>.
</para>