1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

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;