mirror of https://github.com/postgres/postgres
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:
parent
6ab1dbd26b
commit
dc9f8a7983
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -49,6 +49,7 @@ tests += {
|
|||
'planning',
|
||||
'user_activity',
|
||||
'wal',
|
||||
'entry_timestamp',
|
||||
'cleanup',
|
||||
'oldextversions',
|
||||
],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue