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
--- /dev/null
+--
+-- 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)
+
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
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;
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
-- 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,
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'
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;
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
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;
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);
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,
/* 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 */
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;
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();
}
#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.
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));
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ stats_since = e->stats_since;
+ minmax_stats_since = e->minmax_stats_since;
SpinLockRelease(&e->mutex);
}
{
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 :
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;
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;
long num_entries;
long num_remove = 0;
pgssHashKey key;
+ TimestampTz stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
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. */
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)
{
(!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);
}
}
*/
{
volatile pgssSharedState *s = (volatile pgssSharedState *) pgss;
- TimestampTz stats_reset = GetCurrentTimestamp();
SpinLockAcquire(&s->mutex);
s->stats.dealloc = 0;
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
--- /dev/null
+--
+-- 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;
\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;
<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>
<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>
<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>
<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>
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>
<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>
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>