[PATCH] Tracking statements entry timestamp in pg_stat_statements
This is a proposal for a new feature in pg_stat_statements extension.
As a statistical extension providing counters pg_stat_statements
extension is a target for various sampling solutions. All of them
interested in calculation of statement statistics increments between
two samples. But we face a problem here - observing one statement with
its statistics right now we can't be sure that statistics increment for
this statement is continuous from previous sample. This statement could
be deallocated after previous sample and come back soon. Also it could
happen that statement executions after that increased statistics to
above the values we observed in previous sample making it impossible to
detect deallocation on statement level.
My proposition here is to store statement entry timestamp. In this case
any sampling solution in case of returning statement will easily detect
it by changed timestamp value. And for every statement we will know
exact time interval for its statistics.
Attachments:
pg_stat_statements_ts_patch_2021_0322.patchtext/x-patch; charset=UTF-8; name=pg_stat_statements_ts_patch_2021_0322.patchDownload
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 16158525ca..ffe08dece5 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -876,4 +876,26 @@ SELECT dealloc FROM pg_stat_statements_info;
0
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SELECT now() AS start_ts \gset
+SELECT 1 AS "STMTTS";
+ STMTTS
+--------
+ 1
+(1 row)
+
+SELECT count(*) FROM pg_stat_statements WHERE first_seen >= :'start_ts' AND query LIKE '%STMTTS%';
+ count
+-------
+ 1
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql b/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
index 3504ca7eb1..9842d2da49 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
@@ -3,6 +3,60 @@
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.9'" to load this file. \quit
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT first_seen timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_9'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
+
--- Define pg_stat_statements_info
CREATE FUNCTION pg_stat_statements_info(
OUT dealloc bigint,
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 62cccbfa44..70b08d36c8 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -99,7 +99,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201218;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -125,7 +125,8 @@ typedef enum pgssVersion
PGSS_V1_1,
PGSS_V1_2,
PGSS_V1_3,
- PGSS_V1_8
+ PGSS_V1_8,
+ PGSS_V1_9
} pgssVersion;
typedef enum pgssStoreKind
@@ -217,6 +218,7 @@ 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 first_seen; /* timestamp of entry allocation moment */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@@ -337,6 +339,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
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);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -684,6 +687,7 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->first_seen = temp.first_seen;
}
/* Read global statistics for pg_stat_statements */
@@ -1511,7 +1515,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_2 19
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
-#define PG_STAT_STATEMENTS_COLS 32 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_9 33
+#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1523,6 +1528,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_9(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_8(PG_FUNCTION_ARGS)
{
@@ -1642,6 +1657,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_8)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_9:
+ if (api_version != PGSS_V1_9)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1727,6 +1746,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz first_seen;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1793,6 +1813,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ first_seen = e->first_seen;
SpinLockRelease(&e->mutex);
}
@@ -1864,12 +1885,17 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_9)
+ {
+ values[i++] = TimestampTzGetDatum(first_seen);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
+ api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1985,6 +2011,7 @@ 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->first_seen = GetCurrentTimestamp();
}
return entry;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 6f58d9d0f6..590798d133 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -364,4 +364,12 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
SELECT pg_stat_statements_reset();
SELECT dealloc FROM pg_stat_statements_info;
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+SELECT now() AS start_ts \gset
+SELECT 1 AS "STMTTS";
+SELECT count(*) FROM pg_stat_statements WHERE first_seen >= :'start_ts' AND query LIKE '%STMTTS%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 464bf0e5ae..54df2ea73a 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -363,6 +363,15 @@
Total amount of WAL generated by the statement in bytes
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>first_seen</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
Dear Andrei,
I think the idea is good because the pg_stat_statements_info view cannot distinguish
whether the specific statement is deallocated or not.
But multiple calling of GetCurrentTimestamp() may cause some performance issues.
How about adding a configuration parameter for controlling this feature?
Or we don't not have to worry about that?
+ if (api_version >= PGSS_V1_9) + { + values[i++] = TimestampTzGetDatum(first_seen); + }
I think {} is not needed here.
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
Hi Kuroda,
Thank you for your attention!
On Tue, 2021-03-23 at 02:13 +0000, kuroda.hayato@fujitsu.com wrote:
But multiple calling of GetCurrentTimestamp() may cause some
performance issues.
How about adding a configuration parameter for controlling this
feature?
Or we don't not have to worry about that?
Certaily I was thinking about this. And I've taken an advice of Teodor
Sigaev - a much more expirienced developer than me. It seems that
GetCurrentTimestamp() is fast enough for our purpose and we won't call
it too often - only on new statement entry allocation.
By the way right now in my workload tracing tool pg_profile I have to
reset pg_stat_statements on every sample (wich is about 30-60 minutes)
to make sure that all workload between samples is captured. This causes
much more overhead. Introduced first_seen column can eliminate the need
of resets.
However, there is another way - we can store the curent value
of pg_stat_statements_info.dealloc field when allocating a new
statement entry instead of timstamping it. Probably, it would be little
faster, but timestamp seems much more valuable here.
+ if (api_version >= PGSS_V1_9) + { + values[i++] = TimestampTzGetDatum(first_seen); + }I think {} is not needed here.
Of course, thank you!
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On Tue, Mar 23, 2021 at 09:50:16AM +0300, Andrei Zubkov wrote:
By the way right now in my workload tracing tool pg_profile I have to
reset pg_stat_statements on every sample (wich is about 30-60 minutes)
to make sure that all workload between samples is captured. This causes
much more overhead. Introduced first_seen column can eliminate the need
of resets.
Note that you could also detect entries for which some counters decreased (e.g.
the execution count), and in that case only use the current values. It should
give the exact same results as what you will get with the first_seen column,
except of course if some entry is almost never used and is suddenly used a lot
after an explicit reset or an eviction and only until you perform your
snapshot. I'm not sure that it's a very likely scenario though.
FTR that's how powa currently deals with reset/eviction.
Hi Julien,
On Tue, 2021-03-23 at 15:03 +0800, Julien Rouhaud wrote:
Note that you could also detect entries for which some counters
decreased (e.g.
the execution count), and in that case only use the current values.
Yes, but checking condition for several counters seems complicated
compared to check only one field.
It should
give the exact same results as what you will get with the first_seen
column,
except of course if some entry is almost never used and is suddenly
used a lot
after an explicit reset or an eviction and only until you perform
your
snapshot. I'm not sure that it's a very likely scenario though.
But it is possible, and we are guessing here. Storing a timestamp does
not seems too expensive to me, but it totally eliminates guessing, and
provides a clear view about the time interval we watching for this
specific statement.
FTR that's how powa currently deals with reset/eviction.
PoWA sampling is much more frequent than pg_profile. For PoWA it is, of
cource, very unlikely scenario, but still possible.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Dear Andrei,
Certaily I was thinking about this. And I've taken an advice of Teodor
Sigaev - a much more expirienced developer than me. It seems that
GetCurrentTimestamp() is fast enough for our purpose and we won't call
it too often - only on new statement entry allocation.
OK.
However, there is another way - we can store the curent value
of pg_stat_statements_info.dealloc field when allocating a new
statement entry instead of timstamping it. Probably, it would be little
faster, but timestamp seems much more valuable here.
I don't like the idea because such a column has no meaning for the specific row.
I prefer storing timestamp if GetCurrentTimestamp() is cheap.
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
Dear Kuroda,
I don't like the idea because such a column has no meaning for the
specific row.
I prefer storing timestamp if GetCurrentTimestamp() is cheap.
I agree. New version attached.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From ac68636c8a26223854292ee5860f4a5989cb985a Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Tue, 23 Mar 2021 17:03:34 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
Added first_seen column in pg_stat_statements view. This field is
populated with current timestamp when a new statement is added to
pg_stat_statements hashtable. This field provides clean information
about statistics collection time interval for each statement. Besides
it can be used by sampling solutions to detect situations when a
statement was evicted and returned back between two samples.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../expected/pg_stat_statements.out | 22 ++++++++
.../pg_stat_statements--1.8--1.9.sql | 54 +++++++++++++++++++
.../pg_stat_statements/pg_stat_statements.c | 31 +++++++++--
.../sql/pg_stat_statements.sql | 8 +++
doc/src/sgml/pgstatstatements.sgml | 9 ++++
5 files changed, 121 insertions(+), 3 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 16158525ca..ffe08dece5 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -876,4 +876,26 @@ SELECT dealloc FROM pg_stat_statements_info;
0
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+SELECT now() AS start_ts \gset
+SELECT 1 AS "STMTTS";
+ STMTTS
+--------
+ 1
+(1 row)
+
+SELECT count(*) FROM pg_stat_statements WHERE first_seen >= :'start_ts' AND query LIKE '%STMTTS%';
+ count
+-------
+ 1
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql b/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
index 3504ca7eb1..9842d2da49 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.8--1.9.sql
@@ -3,6 +3,60 @@
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.9'" to load this file. \quit
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT first_seen timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_9'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
+
--- Define pg_stat_statements_info
CREATE FUNCTION pg_stat_statements_info(
OUT dealloc bigint,
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 62cccbfa44..090889e21b 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -99,7 +99,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201218;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -125,7 +125,8 @@ typedef enum pgssVersion
PGSS_V1_1,
PGSS_V1_2,
PGSS_V1_3,
- PGSS_V1_8
+ PGSS_V1_8,
+ PGSS_V1_9
} pgssVersion;
typedef enum pgssStoreKind
@@ -217,6 +218,7 @@ 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 first_seen; /* timestamp of entry allocation moment */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@@ -337,6 +339,7 @@ PG_FUNCTION_INFO_V1(pg_stat_statements_reset_1_7);
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);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -684,6 +687,7 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->first_seen = temp.first_seen;
}
/* Read global statistics for pg_stat_statements */
@@ -1511,7 +1515,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_2 19
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
-#define PG_STAT_STATEMENTS_COLS 32 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_9 33
+#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1523,6 +1528,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_9(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_9, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_8(PG_FUNCTION_ARGS)
{
@@ -1642,6 +1657,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_8)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_9:
+ if (api_version != PGSS_V1_9)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1727,6 +1746,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz first_seen;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1793,6 +1813,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ first_seen = e->first_seen;
SpinLockRelease(&e->mutex);
}
@@ -1864,12 +1885,15 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_9)
+ values[i++] = TimestampTzGetDatum(first_seen);
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
api_version == PGSS_V1_2 ? PG_STAT_STATEMENTS_COLS_V1_2 :
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
+ api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1985,6 +2009,7 @@ 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->first_seen = GetCurrentTimestamp();
}
return entry;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 6f58d9d0f6..590798d133 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -364,4 +364,12 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
SELECT pg_stat_statements_reset();
SELECT dealloc FROM pg_stat_statements_info;
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+SELECT now() AS start_ts \gset
+SELECT 1 AS "STMTTS";
+SELECT count(*) FROM pg_stat_statements WHERE first_seen >= :'start_ts' AND query LIKE '%STMTTS%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 464bf0e5ae..54df2ea73a 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -363,6 +363,15 @@
Total amount of WAL generated by the statement in bytes
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>first_seen</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
--
2.26.3
2021-03-23 23:08 に Andrei Zubkov さんは書きました:
Dear Kuroda,
I don't like the idea because such a column has no meaning for the
specific row.
I prefer storing timestamp if GetCurrentTimestamp() is cheap.I agree. New version attached.
Thanks for posting the patch.
I agree with this content.
Is it necessary to update the version of pg_stat_statements now that the
release is targeted for PG15?
We take into account the risk that users will misunderstand.
Regards,
--
Yuki Seino
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION
On Wed, 2021-04-07 at 17:26 +0900, Seino Yuki wrote:
Is it necessary to update the version of pg_stat_statements now that
the
release is targeted for PG15?
I think, yes, version of pg_stat_statements is need to be updated. Is
it will be 1.10 in PG15?
Regards,
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Dear Andrei,
I think, yes, version of pg_stat_statements is need to be updated. Is
it will be 1.10 in PG15?
I think you are right.
According to [1]/messages/by-id/20201202040516.GA43757@nol we can bump up the version per one PG major version,
and any features are not committed yet for 15.
[1]: /messages/by-id/20201202040516.GA43757@nol
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
Hi, Kuroda!
I've intended to change the pg_stat_statements version with rebasing
this patch to the current master branch state. Now this is commit
07e5e66.
But I'm unable to test the patch - it seems that pg_stat_statements is
receiving queryId = 0 for every statements in every hook now and
statements are not tracked at all.
Am I mistaken somewhere? Maybe you know why this is happening?
Thank you!
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Show quoted text
On Fri, 2021-04-09 at 00:23 +0000, kuroda.hayato@fujitsu.com wrote:
Dear Andrei,
I think, yes, version of pg_stat_statements is need to be updated.
Is
it will be 1.10 in PG15?I think you are right.
According to [1] we can bump up the version per one PG major version,
and any features are not committed yet for 15.[1]: /messages/by-id/20201202040516.GA43757@nol
Best Regards,
Hayato Kuroda
FUJITSU LIMITED
Le mer. 14 avr. 2021 à 17:22, Andrei Zubkov <zubkov@moonset.ru> a écrit :
But I'm unable to test the patch - it seems that pg_stat_statements is
receiving queryId = 0 for every statements in every hook now and
statements are not tracked at all.Am I mistaken somewhere? Maybe you know why this is happening?
did you enable compute_query_id new parameter?
Show quoted text
On Wed, 2021-04-14 at 17:32 +0800, Julien Rouhaud wrote:
did you enable compute_query_id new parameter?
Hi, Julien!
Thank you very much! I've missed it.
Show quoted text
Hello, Kuroda!
On Fri, 2021-04-09 at 00:23 +0000, kuroda.hayato@fujitsu.com wrote:
I think you are right.
According to [1] we can bump up the version per one PG major version,
and any features are not committed yet for 15.
Version 2 of patch attached.
pg_stat_statements version is now 1.10 and patch is based on 0f61727.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v2-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v2-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 321ce82f22894be39297515268c1f3bed74778e2 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 14 Apr 2021 16:35:56 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
Added first_seen column in pg_stat_statements view. This field is
populated with current timestamp when a new statement is added to
pg_stat_statements hashtable. This field provides clean information
about statistics collection time interval for each statement. Besides
it can be used by sampling solutions to detect situations when a
statement was evicted and returned back between two samples.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/pg_stat_statements.out | 29 +++++++++
.../pg_stat_statements--1.9--1.10.sql | 59 +++++++++++++++++++
.../pg_stat_statements/pg_stat_statements.c | 31 +++++++++-
.../pg_stat_statements.control | 2 +-
.../sql/pg_stat_statements.sql | 9 +++
doc/src/sgml/pgstatstatements.sgml | 9 +++
7 files changed, 137 insertions(+), 5 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 3ec627b956..7d7b2f4808 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 674ed270a8..f2268dfd87 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -941,4 +941,33 @@ SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%
$$ LANGUAGE plpgsql | | |
(3 rows)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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 first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+ ?column? | count
+----------+-------
+ f | 1
+ t | 1
+(2 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 0000000000..969a30fbdc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,59 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT first_seen timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index f42f07622e..8af97ec694 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -214,6 +215,7 @@ 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 first_seen; /* timestamp of entry allocation moment */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@@ -302,6 +304,7 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -642,6 +645,7 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->first_seen = temp.first_seen;
}
/* Read global statistics for pg_stat_statements */
@@ -1417,7 +1421,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 34
+#define PG_STAT_STATEMENTS_COLS 34 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1429,6 +1434,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1562,6 +1577,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1647,6 +1666,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz first_seen;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1715,6 +1735,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ first_seen = e->first_seen;
SpinLockRelease(&e->mutex);
}
@@ -1786,6 +1807,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ values[i++] = TimestampTzGetDatum(first_seen);
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1793,6 +1816,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1908,6 +1932,7 @@ 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->first_seen = GetCurrentTimestamp();
}
return entry;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed50..0747e48138 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 56d8526ccf..5265e050e4 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -385,4 +385,13 @@ END;
$$ LANGUAGE plpgsql;
SELECT query, toplevel, plans, calls FROM pg_stat_statements WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+SELECT 1 AS "STMTTS1";
+SELECT now() AS ref_ts \gset
+SELECT 1,2 AS "STMTTS2";
+SELECT first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index e235504e9a..a252cc3651 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -380,6 +380,15 @@
Total amount of WAL generated by the statement in bytes
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>first_seen</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
--
2.30.2
The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: tested, passed
Spec compliant: not tested
Documentation: not tested
Hi, Andrei
I tested your patch, and it works well. I also prefer timestamp inseatead of dealloc num.
I think it can provide more useful details about query statements.
Regards,
Martin Sun
Hi, Martin
On Mon, 2021-04-19 at 11:39 +0000, Chengxi Sun wrote:
I tested your patch, and it works well. I also prefer timestamp
inseatead of dealloc num.
I think it can provide more useful details about query statements.
Thank you for your review.
Certainly, timestamp is valuable here. Deallocation number is only a
workaround in unlikely case when timestamping will cost a much. It
seems, that it can happen only when significant amount of statements
causes a new entry in pg_stat_statements hashtable. However, in such
case using of pg_stat_statements extension might be qute difficult.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi, Andrey!
I’ve tried to apply your patch v2-0001 on current master, but i failed.
There were git apply errors at:
pg_stat_statements.out:941
pg_stat_statements.sql:385
Best Regards,
Anton Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi, Anton!
I've corrected the patch and attached a new version.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Show quoted text
On Wed, 2021-10-06 at 18:13 +0300, Мельников Антон Андреевич wrote:
Hi, Andrey!
I’ve tried to apply your patch v2-0001 on current master, but i
failed.
There were git apply errors at:
pg_stat_statements.out:941
pg_stat_statements.sql:385
Best Regards,Anton Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v3-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v3-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From e44b634f21216791c9b5fd9a533532437039dcc1 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 7 Oct 2021 13:07:36 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
Added first_seen column in pg_stat_statements view. This field is
populated with current timestamp when a new statement is added to
pg_stat_statements hashtable. This field provides clean information
about statistics collection time interval for each statement. Besides
it can be used by sampling solutions to detect situations when a
statement was evicted and returned back between two samples.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/pg_stat_statements.out | 29 +++++++++
.../pg_stat_statements--1.9--1.10.sql | 59 +++++++++++++++++++
.../pg_stat_statements/pg_stat_statements.c | 31 +++++++++-
.../pg_stat_statements.control | 2 +-
.../sql/pg_stat_statements.sql | 9 +++
doc/src/sgml/pgstatstatements.sgml | 9 +++
7 files changed, 137 insertions(+), 5 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38..edc40c8bbf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index b52d187722..c5b5d8082f 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,33 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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 first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+ ?column? | count
+----------+-------
+ f | 1
+ t | 1
+(2 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 0000000000..969a30fbdc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,59 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT first_seen timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 07fe0e7cda..88cadd435f 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -214,6 +215,7 @@ 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 first_seen; /* timestamp of entry allocation moment */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@@ -302,6 +304,7 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -649,6 +652,7 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->first_seen = temp.first_seen;
}
/* Read global statistics for pg_stat_statements */
@@ -1426,7 +1430,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 34
+#define PG_STAT_STATEMENTS_COLS 34 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1438,6 +1443,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1571,6 +1586,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1656,6 +1675,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz first_seen;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1724,6 +1744,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ first_seen = e->first_seen;
SpinLockRelease(&e->mutex);
}
@@ -1795,6 +1816,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ values[i++] = TimestampTzGetDatum(first_seen);
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1802,6 +1825,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1917,6 +1941,7 @@ 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->first_seen = GetCurrentTimestamp();
}
return entry;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed50..0747e48138 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c18..44a766c9a8 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,13 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+SELECT 1 AS "STMTTS1";
+SELECT now() AS ref_ts \gset
+SELECT 1,2 AS "STMTTS2";
+SELECT first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 70b28a92c1..6484fb6298 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -379,6 +379,15 @@
Total amount of WAL generated by the statement in bytes
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>first_seen</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
--
2.30.2
There is an issue with this patch. It's main purpose is the ability to
calculate values of pg_stat_statements view for a time period between
two samples without resetting pg_stat_statements being absolutely sure
that the statement was not evicted.
Such approach solves the problem for metrics with except of min and max
time values. It seems that partial reset is needed here resetting
min/max values during a sample. But overall min/max values will be lost
in this case. Does addition of resettable min/max metrics to the
pg_stat_statemets view seems reasonable here?
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On 07.10.2021 15:31, Andrei Zubkov wrote:
There is an issue with this patch. It's main purpose is the ability to
calculate values of pg_stat_statements view
[...]
Does addition of resettable min/max metrics to the
pg_stat_statemets view seems reasonable here?
Hello, Andrey!
I think it depends on what the slow top level sampler wants.
Let define the current values in pg_stat_statements for some query as gmin and gmax.
It seems to be a two main variants:
1) If top level sampler wants to know for some query the min and max values for
the entire watch time, then existing gmin and gmax in pg_stat_statements are sufficient.
The top level sampler can clarify top_min and top_max at every slow sample as follows:
top_max = gmax > top_max ? gmax : top_max;
top_min = gmin < top_min ? gmin : top_min;
This should work regardless of whether there was a reset between samples or not.
2) The second case happens when the top level sampler wants to know the min and max
values for sampling period.
In that case i think one shouldn't not use gmin and gmax and especially reset
them asynchronously from outside because its lifetime and sampling period are
independent values and moreover someone else might need gmin and gmax as is.
So i suppose that additional vars loc_min and loc_max is a right way to do it.
If that, at every top sample one need to replace not clarify
the new top values as follows:
top_max = loc_max; loc_max = 0;
top_min = loc_min; loc_min = INT_MAX;
And one more thing, if there was a reset of stats between two samples,
then i think it is the best to ignore the new values,
since they were obtained for an incomplete period.
This patch, thanks to the saved time stamp, makes possible
to determine the presence of reset between samples and
there should not be a problem to realize such algorithm.
The patch is now applied normally, all my tests were successful.
The only thing I could suggest to your notice
is a small cosmetic edit to replace
the numeric value in #define on line 1429 of pg_stat_statements.c
by one of the constants defined above.
Best regards,
Anton Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi, Anton!
Thank you for your review!
On Mon, 2021-10-18 at 22:11 +0300, Anton A. Melnikov wrote:
So i suppose that additional vars loc_min and loc_max is a right way
to do it.
I've added the following fields to the pg_stat_statements view:
min_plan_time_local float8,
max_plan_time_local float8,
min_exec_time_local float8,
max_exec_time_local float8
and a function that is able to reset those fields:
CREATE FUNCTION pg_stat_statements_reset_local(IN userid Oid DEFAULT 0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0
)
It resets the local fields mentioned above and updates the new field
local_stats_since timestamp with time zone
with the current timestamp. All other statement statistics are remains
unchanged. After the reset _local fields will have NULL values till the
next statement execution.
And one more thing, if there was a reset of stats between two
samples,
then i think it is the best to ignore the new values,
since they were obtained for an incomplete period.
This patch, thanks to the saved time stamp, makes possible
to determine the presence of reset between samples and
there should not be a problem to realize such algorithm.
Yes, it seems this is up to the sampling solution. Maybe in some cases
incomplete information will be better than nothing... Anyway we have
all necessary data now.
The only thing I could suggest to your notice
is a small cosmetic edit to replace
the numeric value in #define on line 1429 of pg_stat_statements.c
by one of the constants defined above.
Hmm. I've left it just like it was before me. But it seems, you are
right.
I've attached a new version of a patch. The first_seen column was
renamed to stats_since - it seems to be more self-explaining to me. But
I'm not sure in the current naming at all.
The tests is not ready yet, but any thoughts about the patch are
welcome right now.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v4-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v4-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From bea0ae9fd062ee1810c9ff2d5f1be98a19114d84 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 3 Dec 2021 16:25:17 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This column
is populated with the current timestamp when a new statement is added to the
pg_stat_statements hashtable. This column provides clean information about
statistics collection time interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between two samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the following columns:
min_plan_time_local float8,
max_plan_time_local float8,
min_exec_time_local float8,
max_exec_time_local float8
And a function to reset those statistics during a sample:
CREATE FUNCTION pg_stat_statements_reset_local(IN userid Oid DEFAULT 0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0
)
The timestamp of last local statistics reset is stored for every statement in
a column:
local_stats_since timestamp with time zone
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/pg_stat_statements.out | 29 +++
.../pg_stat_statements--1.9--1.10.sql | 72 +++++++
.../pg_stat_statements/pg_stat_statements.c | 200 +++++++++++++++++-
.../pg_stat_statements.control | 2 +-
.../sql/pg_stat_statements.sql | 9 +
doc/src/sgml/pgstatstatements.sgml | 111 +++++++++-
7 files changed, 419 insertions(+), 7 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38..edc40c8bbf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6..9388490073 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,33 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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 first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+ ?column? | count
+----------+-------
+ f | 1
+ t | 1
+(2 rows)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 0000000000..00f5f1c933
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,72 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT min_plan_time_local float8,
+ OUT max_plan_time_local float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT min_exec_time_local float8,
+ OUT max_exec_time_local float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT stats_since timestamp with time zone,
+ OUT local_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+CREATE FUNCTION pg_stat_statements_reset_local(IN userid Oid DEFAULT 0,
+ IN dbid Oid DEFAULT 0,
+ IN queryid bigint DEFAULT 0
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'pg_stat_statements_reset_local_1_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 726ba59e2b..43467da0eb 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -168,6 +169,10 @@ typedef struct Counters
* msec */
double max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
* msec */
+ double min_time_local[PGSS_NUMKIND]; /* minimum planning/execution time
+ * since local reset in msec */
+ double max_time_local[PGSS_NUMKIND]; /* maximum planning/execution time
+ * since local reset in msec */
double mean_time[PGSS_NUMKIND]; /* mean planning/execution time in
* msec */
double sum_var_time[PGSS_NUMKIND]; /* sum of variances in
@@ -214,6 +219,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 gstats_since; /* timestamp of entry allocation moment */
+ TimestampTz lstats_since; /* timestamp of last local statistics reset */
slock_t mutex; /* protects the counters only */
} pgssEntry;
@@ -298,10 +305,12 @@ void _PG_fini(void);
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_local_1_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,6 +354,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_local(Oid userid, Oid dbid, uint64 queryid);
static void entry_reset(Oid userid, Oid dbid, uint64 queryid);
static char *generate_normalized_query(JumbleState *jstate, const char *query,
int query_loc, int *query_len_p);
@@ -649,6 +659,8 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->gstats_since = temp.gstats_since;
+ entry->lstats_since = temp.lstats_since;
}
/* Read global statistics for pg_stat_statements */
@@ -1340,6 +1352,8 @@ pgss_store(const char *query, uint64 queryId,
e->counters.min_time[kind] = total_time;
e->counters.max_time[kind] = total_time;
e->counters.mean_time[kind] = total_time;
+ e->counters.min_time_local[kind] = total_time;
+ e->counters.max_time_local[kind] = total_time;
}
else
{
@@ -1359,6 +1373,23 @@ pgss_store(const char *query, uint64 queryId,
e->counters.min_time[kind] = total_time;
if (e->counters.max_time[kind] < total_time)
e->counters.max_time[kind] = total_time;
+
+ /*
+ * Calculate local min and max time. min_local > max_local
+ * means that local reset was happen
+ */
+ if (e->counters.min_time_local[kind] > e->counters.max_time_local[kind])
+ {
+ e->counters.min_time_local[kind] = total_time;
+ e->counters.max_time_local[kind] = total_time;
+ }
+ else
+ {
+ if (e->counters.min_time_local[kind] > total_time)
+ e->counters.min_time_local[kind] = total_time;
+ if (e->counters.max_time_local[kind] < total_time)
+ e->counters.max_time_local[kind] = total_time;
+ }
}
e->counters.rows += rows;
e->counters.shared_blks_hit += bufusage->shared_blks_hit;
@@ -1389,6 +1420,25 @@ done:
pfree(norm_query);
}
+/*
+ * Reset local statement statistics corresponding to userid, dbid, and queryid.
+ */
+Datum
+pg_stat_statements_reset_local_1_10(PG_FUNCTION_ARGS)
+{
+ Oid userid;
+ Oid dbid;
+ uint64 queryid;
+
+ userid = PG_GETARG_OID(0);
+ dbid = PG_GETARG_OID(1);
+ queryid = (uint64) PG_GETARG_INT64(2);
+
+ entry_reset_local(userid, dbid, queryid);
+
+ PG_RETURN_VOID();
+}
+
/*
* Reset statement statistics corresponding to userid, dbid, and queryid.
*/
@@ -1426,7 +1476,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 39
+#define PG_STAT_STATEMENTS_COLS 39 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1438,6 +1489,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1571,6 +1632,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1656,6 +1721,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz gstats_since;
+ TimestampTz lstats_since;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1724,6 +1791,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ gstats_since = e->gstats_since;
+ lstats_since = e->lstats_since;
SpinLockRelease(&e->mutex);
}
@@ -1758,6 +1827,21 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
else
stddev = 0.0;
values[i++] = Float8GetDatumFast(stddev);
+
+ if (tmp.max_time_local[kind] < tmp.min_time_local[kind])
+ {
+ /*
+ * There was not executions since last local min/max reset
+ * so, we should return nulls.
+ */
+ nulls[i++] = true;
+ nulls[i++] = true;
+ }
+ else
+ {
+ values[i++] = Float8GetDatumFast(tmp.min_time_local[kind]);
+ values[i++] = Float8GetDatumFast(tmp.max_time_local[kind]);
+ }
}
}
values[i++] = Int64GetDatumFast(tmp.rows);
@@ -1795,6 +1879,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(gstats_since);
+ values[i++] = TimestampTzGetDatum(lstats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1802,6 +1891,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1917,6 +2007,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->gstats_since = GetCurrentTimestamp();
+ entry->lstats_since = entry->gstats_since;
}
return entry;
@@ -2463,6 +2555,106 @@ gc_fail:
record_gc_qtexts();
}
+/*
+ * Reset local statistic values of specified entries
+ */
+static void
+entry_reset_local(Oid userid, Oid dbid, uint64 queryid)
+{
+ HASH_SEQ_STATUS hash_seq;
+ pgssEntry *entry;
+ Counters *entry_counters;
+ pgssHashKey key;
+ TimestampTz stats_reset_local;
+
+ if (!pgss || !pgss_hash)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
+
+ LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
+
+ stats_reset_local = GetCurrentTimestamp();
+
+ if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
+ {
+ /* If all the parameters are available, use the fast path. */
+ memset(&key, 0, sizeof(pgssHashKey));
+ key.userid = userid;
+ key.dbid = dbid;
+ key.queryid = queryid;
+
+ /*
+ * Reset locals, starting with the nested-level entry
+ * For min/max values reset sign is min > max
+ */
+ key.toplevel = false;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time_local[kind] = 0;
+ entry_counters->min_time_local[kind] = 1;
+ }
+ entry->lstats_since = stats_reset_local;
+ }
+
+ /* Reset locals for top level statements */
+ key.toplevel = true;
+
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time_local[kind] = 0;
+ entry_counters->min_time_local[kind] = 1;
+ }
+ entry->lstats_since = stats_reset_local;
+ }
+ }
+ else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
+ {
+ /* Reset locals for entries corresponding to valid parameters. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ if ((!userid || entry->key.userid == userid) &&
+ (!dbid || entry->key.dbid == dbid) &&
+ (!queryid || entry->key.queryid == queryid))
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time_local[kind] = 0;
+ entry_counters->min_time_local[kind] = 1;
+ }
+ entry->lstats_since = stats_reset_local;
+ }
+ }
+ }
+ else
+ {
+ /* Reset locals for all entries. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time_local[kind] = 0;
+ entry_counters->min_time_local[kind] = 1;
+ }
+ entry->lstats_since = stats_reset_local;
+ }
+ }
+
+ LWLockRelease(pgss->lock);
+}
+
/*
* Release entries corresponding to parameters passed.
*/
@@ -2492,7 +2684,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Remove the key if it exists, starting with the nested-level entry */
key.toplevel = false;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
if (entry) /* found */
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed50..0747e48138 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c18..44a766c9a8 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,13 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+SELECT 1 AS "STMTTS1";
+SELECT now() AS ref_ts \gset
+SELECT 1,2 AS "STMTTS2";
+SELECT first_seen >= :'ref_ts', count(*) FROM pg_stat_statements WHERE query LIKE '%STMTTS%' GROUP BY first_seen >= :'ref_ts' ORDER BY first_seen >= :'ref_ts';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index bc9d5bdbe3..b10c24d212 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -30,7 +30,8 @@
these statistics, the module provides views
<structname>pg_stat_statements</structname> and
<structname>pg_stat_statements_info</structname>,
- and the utility functions <function>pg_stat_statements_reset</function> and
+ and the utility functions <function>pg_stat_statements_reset</function>,
+ <function>pg_stat_statements_reset_local</function> and
<function>pg_stat_statements</function>. These are not available globally but
can be enabled for a specific database with
<command>CREATE EXTENSION pg_stat_statements</command>.
@@ -180,6 +181,38 @@
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>min_plan_time_local</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Minimum time spent planning the statement since
+ <structfield>local_stats_since</structfield>, in milliseconds
+ (if <varname>pg_stat_statements.track_planning</varname> is enabled,
+ otherwise zero)
+ </para>
+ <para>
+ Function <function>pg_stat_statements_reset_local</function> resets this
+ value
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>max_plan_time_local</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Maximum time spent planning the statement since
+ <structfield>local_stats_since</structfield>, in milliseconds
+ (if <varname>pg_stat_statements.track_planning</varname> is enabled,
+ otherwise zero)
+ </para>
+ <para>
+ Function <function>pg_stat_statements_reset_local</function> resets this
+ value
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>calls</structfield> <type>bigint</type>
@@ -234,6 +267,34 @@
</para></entry>
</row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>min_exec_time_local</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Minimum time spent executing the statement since
+ <structfield>local_stats_since</structfield>, in milliseconds
+ </para>
+ <para>
+ Function <function>pg_stat_statements_reset_local</function> resets this
+ value
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>max_exec_time_local</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Maximum time spent executing the statement since
+ <structfield>local_stats_since</structfield>, in milliseconds
+ </para>
+ <para>
+ Function <function>pg_stat_statements_reset_local</function> resets this
+ value
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rows</structfield> <type>bigint</type>
@@ -379,6 +440,23 @@
Total amount of WAL generated by the statement in bytes
</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>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>local_stats_since</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of local statistics gathering start for the statement.
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -595,6 +673,37 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <function>pg_stat_statements_reset_local(userid Oid, dbid Oid, queryid bigint) returns void</function>
+ <indexterm>
+ <primary>pg_stat_statements_reset_local</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ <function>pg_stat_statements_reset_local</function> discards only the local
+ statistics (<structfield>min_plan_time_local</structfield>,
+ <structfield>max_plan_time_local</structfield>,
+ <structfield>min_exec_time_local</structfield> and
+ <structfield>max_exec_time_local</structfield>)
+ gathered so far by <filename>pg_stat_statements</filename> corresponding
+ to the specified <structfield>userid</structfield>, <structfield>dbid</structfield>
+ and <structfield>queryid</structfield>. If any of the parameters are not
+ specified, the default value <literal>0</literal>(invalid) is used for
+ each of them and the local statistics that match with other parameters will be
+ reset. If no parameter is specified or all the specified parameters are
+ <literal>0</literal>(invalid), it will discard all local statistics.
+ Discarding the local statistics of a statement this function will update
+ the value of <structfield>local_stats_since</structfield> with the current
+ timestamp.
+ By default, this function can only be executed by superusers.
+ Access may be granted to others using <command>GRANT</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<function>pg_stat_statements(showtext boolean) returns setof record</function>
--
2.30.2
On Fri, 2021-12-03 at 17:03 +0300, Andrei Zubkov wrote:
I've added the following fields to the pg_stat_statements view:
min_plan_time_local float8,
max_plan_time_local float8,
min_exec_time_local float8,
max_exec_time_local float8and a function that is able to reset those fields:
CREATE FUNCTION pg_stat_statements_reset_local(IN userid Oid DEFAULT
0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0
)It resets the local fields mentioned above and updates the new field
local_stats_since timestamp with time zone
with the current timestamp. All other statement statistics are
remains
unchanged.
After adding new fields to pg_stat_statements view it looks a little
bit overloaded. Furthermore, fields in this view have different
behavior.
What if we'll create a new view for such resettable fields? It will
make description of views and reset functions in the docs much more
clear.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
On 03.12.2021 19:55, Andrei Zubkov wrote:
On Fri, 2021-12-03 at 17:03 +0300, Andrei Zubkov wrote:
...
What if we'll create a new view for such resettable fields? It will
make description of views and reset functions in the docs much more
clear.
Hi, Andrey!
I completely agree that creating a separate view for these new fields is
the most correct thing to do.
As for variable names, the term global is already used for global
statistics, in particular in the struct pgssGlobalStats.
The considered timestamps refer to per statement level
as pointed out in the struct pgssEntry's comment. Therefore, i think
it's better to rename gstats_since to just stats_reset in the same way.
Also it might be better to use the term 'auxiliary' and use the same
approach as for existent similar vars.
So internally it might look something like this:
double aux_min_time[PGSS_NUMKIND];
double aux_max_time[PGSS_NUMKIND];
TimestampTz aux_stats_reset;
And at the view level:
aux_min_plan_time float8,
aux_max_plan_time float8,
aux_min_exec_time float8,
aux_max_exec_time float8,
aux_stats_reset timestamp with time zone
Functions names might be pg_stat_statements_aux() and
pg_stat_statements_aux_reset().
The top-level application may find out period the aux extrema were
collected by determining which reset was closer as follows:
data_collect_period = aux_stats_reset > stats_reset ?
now - aux_stats_reset : now - stats_reset;
and decide not to trust this data if the period was too small.
For correct work aux_stats_reset must be updated and aux extrema values
must be reset simultaneously with updating of stats_reset therefore some
synchronization needed to avoid race with possible external reset.
I've tested the patch v4 and didn't find any evident problems.
Contrib installcheck said:
test pg_stat_statements ... ok 163 ms
test oldextversions ... ok 46 ms
With best regards,
--
Anton A. Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi,
On 2021-12-03 17:03:46 +0300, Andrei Zubkov wrote:
I've attached a new version of a patch.
This fails with an assertion failure:
https://cirrus-ci.com/task/5567540742062080?logs=cores#L55
Greetings,
Andres Freund
Hello,
On Sun, 2022-01-02 at 13:28 -0800, Andres Freund wrote:
Hi,
This fails with an assertion failure:
https://cirrus-ci.com/task/5567540742062080?logs=cores#L55
Andres, thank you for your test! I've missed it. Fixed in attached
patch v5.
On Wed, 2021-12-22 at 04:25 +0300, Anton A. Melnikov wrote:
I completely agree that creating a separate view for these new fields
is
the most correct thing to do.
Anton,
I've created a new view named pg_stat_statements_aux. But for now both
views are using the same function pg_stat_statements which returns all
fields. It seems reasonable to me - if sampling solution will need all
values it can query the function.
Also it might be better to use the term 'auxiliary' and use the same
approach as for existent similar vars.
Agreed, renamed to auxiliary term.
So internally it might look something like this:
double aux_min_time[PGSS_NUMKIND];
double aux_max_time[PGSS_NUMKIND];
TimestampTz aux_stats_reset;And at the view level:
aux_min_plan_time float8,
aux_max_plan_time float8,
aux_min_exec_time float8,
aux_max_exec_time float8,
aux_stats_reset timestamp with time zoneFunctions names might be pg_stat_statements_aux() and
pg_stat_statements_aux_reset().
But it seems "stats_reset" term is not quite correct in this case. The
"stats_since" field holds the timestamp of hashtable entry, but not the
reset time. The same applies to aux_stats_since - for new statement it
holds its entry time, but in case of reset it will hold the reset time.
"stats_reset" name seems a little bit confusing to me.
Attached patch v5
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v5-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v5-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From e3140e136bab9d44d7818ed86e1c8ff119936532 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 14 Jan 2022 18:04:53 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds auxiliary statistics to the pg_stat_statements
function and a new view named pg_stat_statements_aux:
View "public.pg_stat_statements_aux"
Column | Type | Collation | Nullable | Default
-------------------+--------------------------+-----------+----------+---------
userid | oid | | |
dbid | oid | | |
toplevel | boolean | | |
queryid | bigint | | |
query | text | | |
aux_min_plan_time | double precision | | |
aux_max_plan_time | double precision | | |
aux_min_exec_time | double precision | | |
aux_max_exec_time | double precision | | |
stats_since | timestamp with time zone | | |
aux_stats_since | timestamp with time zone | | |
These auxiliary statistics are resettable independently of statement reset by
the new function pg_stat_statements_aux_reset(userid oid, dbid oid, queryid bigint)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/pg_stat_statements.out | 90 ++++++++
.../pg_stat_statements--1.9--1.10.sql | 122 +++++++++++
.../pg_stat_statements/pg_stat_statements.c | 203 +++++++++++++++++-
.../pg_stat_statements.control | 2 +-
.../sql/pg_stat_statements.sql | 50 +++++
doc/src/sgml/pgstatstatements.sgml | 184 +++++++++++++++-
7 files changed, 640 insertions(+), 14 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..34d65bb521c 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,94 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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)
+
+-- auxiliary statistics reset
+-- Is there any statements with different regular and auxiliary values?
+SELECT count(*) > 0 FROM pg_stat_statements(true)
+WHERE
+ ROW(min_plan_time, max_plan_time, min_exec_time, max_exec_time)
+ IS DISTINCT FROM
+ ROW(aux_min_plan_time, aux_max_plan_time, aux_min_exec_time,
+ aux_max_exec_time);
+ ?column?
+----------
+ f
+(1 row)
+
+-- Move reference point
+SELECT now() AS ref_ts \gset
+-- Testing stats_since and aux_stats_since values before and after reset
+SELECT
+ /*pgss_aux_reset_test_query*/
+ bool_and(stats_since <= :'ref_ts') AS all_stats_before_ref,
+ bool_and(aux_stats_since >= :'ref_ts') AS all_aux_stats_after_ref
+FROM pg_stat_statements_aux
+WHERE query NOT LIKE '%pgss_aux_reset_test_query%';
+ all_stats_before_ref | all_aux_stats_after_ref
+----------------------+-------------------------
+ t | f
+(1 row)
+
+-- perform auxiliary statistics reset
+SELECT /*pgss_aux_reset_test_query*/ pg_stat_statements_aux_reset();
+ pg_stat_statements_aux_reset
+------------------------------
+
+(1 row)
+
+SELECT
+ /*pgss_aux_reset_test_query*/
+ bool_and(stats_since <= :'ref_ts') AS all_stats_before_ref,
+ bool_and(aux_stats_since >= :'ref_ts') AS all_aux_stats_after_ref
+FROM pg_stat_statements_aux
+WHERE query NOT LIKE '%pgss_aux_reset_test_query%';
+ all_stats_before_ref | all_aux_stats_after_ref
+----------------------+-------------------------
+ t | t
+(1 row)
+
+-- Is there any statements with different regular and auxiliary
+-- values after aux reset?
+SELECT count(*) > 0 FROM pg_stat_statements(true)
+WHERE
+ ROW(min_plan_time, max_plan_time, min_exec_time, max_exec_time)
+ IS DISTINCT FROM
+ ROW(aux_min_plan_time, aux_max_plan_time, aux_min_exec_time,
+ aux_max_exec_time);
+ ?column?
+----------
+ t
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..cf59108dc92
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,122 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT aux_min_plan_time float8,
+ OUT aux_max_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT aux_min_exec_time float8,
+ OUT aux_max_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT stats_since timestamp with time zone,
+ OUT aux_stats_since timestamp with time zone
+)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pg_stat_statements_1_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since
+ FROM pg_stat_statements(true);
+
+CREATE VIEW pg_stat_statements_aux AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ aux_min_plan_time,
+ aux_max_plan_time,
+ aux_min_exec_time,
+ aux_max_exec_time,
+ stats_since,
+ aux_stats_since
+ FROM pg_stat_statements(true);
+
+CREATE FUNCTION pg_stat_statements_aux_reset(IN userid Oid DEFAULT 0,
+ IN dbid Oid DEFAULT 0,
+ IN queryid bigint DEFAULT 0
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'pg_stat_statements_aux_reset_1_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements, pg_stat_statements_aux TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 082bfa8f77f..5ad90bad5e8 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -168,6 +169,10 @@ typedef struct Counters
* msec */
double max_time[PGSS_NUMKIND]; /* maximum planning/execution time in
* msec */
+ double aux_min_time[PGSS_NUMKIND]; /* minimum planning/execution time
+ * since aux reset in msec */
+ double aux_max_time[PGSS_NUMKIND]; /* maximum planning/execution time
+ * since aux reset in msec */
double mean_time[PGSS_NUMKIND]; /* mean planning/execution time in
* msec */
double sum_var_time[PGSS_NUMKIND]; /* sum of variances in
@@ -209,12 +214,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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 moment */
+ TimestampTz aux_stats_since; /* timestamp of last auxiliary statistics reset */
+ slock_t mutex; /* protects the counters only */
} pgssEntry;
/*
@@ -298,10 +305,12 @@ void _PG_fini(void);
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_aux_reset_1_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,6 +354,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_aux(Oid userid, Oid dbid, uint64 queryid);
static void entry_reset(Oid userid, Oid dbid, uint64 queryid);
static char *generate_normalized_query(JumbleState *jstate, const char *query,
int query_loc, int *query_len_p);
@@ -649,6 +659,8 @@ pgss_shmem_startup(void)
/* copy in the actual stats */
entry->counters = temp.counters;
+ entry->stats_since = temp.stats_since;
+ entry->aux_stats_since = temp.aux_stats_since;
}
/* Read global statistics for pg_stat_statements */
@@ -1336,6 +1348,8 @@ pgss_store(const char *query, uint64 queryId,
e->counters.min_time[kind] = total_time;
e->counters.max_time[kind] = total_time;
e->counters.mean_time[kind] = total_time;
+ e->counters.aux_min_time[kind] = total_time;
+ e->counters.aux_max_time[kind] = total_time;
}
else
{
@@ -1355,6 +1369,24 @@ pgss_store(const char *query, uint64 queryId,
e->counters.min_time[kind] = total_time;
if (e->counters.max_time[kind] < total_time)
e->counters.max_time[kind] = total_time;
+
+ /*
+ * Calculate auxiliary min and max time. aux_min == aux_max == 0
+ * means that auxiliary stats reset was happen
+ */
+ if (e->counters.aux_min_time[kind] == e->counters.aux_max_time[kind]
+ && e->counters.aux_max_time[kind] == 0)
+ {
+ e->counters.aux_min_time[kind] = total_time;
+ e->counters.aux_max_time[kind] = total_time;
+ }
+ else
+ {
+ if (e->counters.aux_min_time[kind] > total_time)
+ e->counters.aux_min_time[kind] = total_time;
+ if (e->counters.aux_max_time[kind] < total_time)
+ e->counters.aux_max_time[kind] = total_time;
+ }
}
e->counters.rows += rows;
e->counters.shared_blks_hit += bufusage->shared_blks_hit;
@@ -1385,6 +1417,25 @@ done:
pfree(norm_query);
}
+/*
+ * Reset auxiliary statement statistics corresponding to userid, dbid, and queryid.
+ */
+Datum
+pg_stat_statements_aux_reset_1_10(PG_FUNCTION_ARGS)
+{
+ Oid userid;
+ Oid dbid;
+ uint64 queryid;
+
+ userid = PG_GETARG_OID(0);
+ dbid = PG_GETARG_OID(1);
+ queryid = (uint64) PG_GETARG_INT64(2);
+
+ entry_reset_aux(userid, dbid, queryid);
+
+ PG_RETURN_VOID();
+}
+
/*
* Reset statement statistics corresponding to userid, dbid, and queryid.
*/
@@ -1422,7 +1473,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 39
+#define PG_STAT_STATEMENTS_COLS 39 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1486,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1567,6 +1629,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1652,6 +1718,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Counters tmp;
double stddev;
int64 queryid = entry->key.queryid;
+ TimestampTz stats_since;
+ TimestampTz aux_stats_since;
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
@@ -1720,6 +1788,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
SpinLockAcquire(&e->mutex);
tmp = e->counters;
+ stats_since = e->stats_since;
+ aux_stats_since = e->aux_stats_since;
SpinLockRelease(&e->mutex);
}
@@ -1754,6 +1824,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
else
stddev = 0.0;
values[i++] = Float8GetDatumFast(stddev);
+
+ if (api_version >= PGSS_V1_10) {
+ values[i++] = Float8GetDatumFast(tmp.aux_min_time[kind]);
+ values[i++] = Float8GetDatumFast(tmp.aux_max_time[kind]);
+ }
}
}
values[i++] = Int64GetDatumFast(tmp.rows);
@@ -1791,6 +1866,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(aux_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1798,6 +1878,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
@@ -1913,6 +1994,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->aux_stats_since = entry->stats_since;
}
return entry;
@@ -2459,6 +2542,106 @@ gc_fail:
record_gc_qtexts();
}
+/*
+ * Reset auxiliary statistic values of specified entries
+ */
+static void
+entry_reset_aux(Oid userid, Oid dbid, uint64 queryid)
+{
+ HASH_SEQ_STATUS hash_seq;
+ pgssEntry *entry;
+ Counters *entry_counters;
+ pgssHashKey key;
+ TimestampTz aux_stats_reset;
+
+ if (!pgss || !pgss_hash)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
+
+ LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
+
+ aux_stats_reset = GetCurrentTimestamp();
+
+ if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
+ {
+ /* If all the parameters are available, use the fast path. */
+ memset(&key, 0, sizeof(pgssHashKey));
+ key.userid = userid;
+ key.dbid = dbid;
+ key.queryid = queryid;
+
+ /*
+ * Reset aux stats, starting with the nested-level entry
+ * For min/max values reset sign is min = max = 0
+ */
+ key.toplevel = false;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->aux_max_time[kind] = 0;
+ entry_counters->aux_min_time[kind] = 0;
+ }
+ entry->aux_stats_since = aux_stats_reset;
+ }
+
+ /* Reset aux stats for top level statements */
+ key.toplevel = true;
+
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->aux_max_time[kind] = 0;
+ entry_counters->aux_min_time[kind] = 0;
+ }
+ entry->aux_stats_since = aux_stats_reset;
+ }
+ }
+ else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
+ {
+ /* Reset aux stats for entries corresponding to valid parameters. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ if ((!userid || entry->key.userid == userid) &&
+ (!dbid || entry->key.dbid == dbid) &&
+ (!queryid || entry->key.queryid == queryid))
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->aux_max_time[kind] = 0;
+ entry_counters->aux_min_time[kind] = 0;
+ }
+ entry->aux_stats_since = aux_stats_reset;
+ }
+ }
+ }
+ else
+ {
+ /* Reset aux stats for all entries. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->aux_max_time[kind] = 0;
+ entry_counters->aux_min_time[kind] = 0;
+ }
+ entry->aux_stats_since = aux_stats_reset;
+ }
+ }
+
+ LWLockRelease(pgss->lock);
+}
+
/*
* Release entries corresponding to parameters passed.
*/
@@ -2488,7 +2671,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Remove the key if it exists, starting with the nested-level entry */
key.toplevel = false;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
if (entry) /* found */
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..603b4207375 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,54 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+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';
+
+-- auxiliary statistics reset
+-- Is there any statements with different regular and auxiliary values?
+SELECT count(*) > 0 FROM pg_stat_statements(true)
+WHERE
+ ROW(min_plan_time, max_plan_time, min_exec_time, max_exec_time)
+ IS DISTINCT FROM
+ ROW(aux_min_plan_time, aux_max_plan_time, aux_min_exec_time,
+ aux_max_exec_time);
+
+-- Move reference point
+SELECT now() AS ref_ts \gset
+
+-- Testing stats_since and aux_stats_since values before and after reset
+SELECT
+ /*pgss_aux_reset_test_query*/
+ bool_and(stats_since <= :'ref_ts') AS all_stats_before_ref,
+ bool_and(aux_stats_since >= :'ref_ts') AS all_aux_stats_after_ref
+FROM pg_stat_statements_aux
+WHERE query NOT LIKE '%pgss_aux_reset_test_query%';
+-- perform auxiliary statistics reset
+SELECT /*pgss_aux_reset_test_query*/ pg_stat_statements_aux_reset();
+
+SELECT
+ /*pgss_aux_reset_test_query*/
+ bool_and(stats_since <= :'ref_ts') AS all_stats_before_ref,
+ bool_and(aux_stats_since >= :'ref_ts') AS all_aux_stats_after_ref
+FROM pg_stat_statements_aux
+WHERE query NOT LIKE '%pgss_aux_reset_test_query%';
+
+-- Is there any statements with different regular and auxiliary
+-- values after aux reset?
+SELECT count(*) > 0 FROM pg_stat_statements(true)
+WHERE
+ ROW(min_plan_time, max_plan_time, min_exec_time, max_exec_time)
+ IS DISTINCT FROM
+ ROW(aux_min_plan_time, aux_max_plan_time, aux_min_exec_time,
+ aux_max_exec_time);
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index bc9d5bdbe3b..0797f324593 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -28,9 +28,11 @@
When <filename>pg_stat_statements</filename> is active, it tracks
statistics across all databases of the server. To access and manipulate
these statistics, the module provides views
- <structname>pg_stat_statements</structname> and
+ <structname>pg_stat_statements</structname>,
+ <structname>pg_stat_statements_aux</structname> and
<structname>pg_stat_statements_info</structname>,
- and the utility functions <function>pg_stat_statements_reset</function> and
+ and the utility functions <function>pg_stat_statements_reset</function>,
+ <function>pg_stat_statements_reset_aux</function> and
<function>pg_stat_statements</function>. These are not available globally but
can be enabled for a specific database with
<command>CREATE EXTENSION pg_stat_statements</command>.
@@ -379,6 +381,15 @@
Total amount of WAL generated by the statement in bytes
</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>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -508,6 +519,154 @@
</para>
</sect2>
+ <sect2>
+ <title>The <structname>pg_stat_statements_aux</structname> View</title>
+
+ <para>
+ The view named <structname>pg_stat_statements_aux</structname> privodes access to
+ auxiliary statistics. These statistics can be reset by the function named
+ <function>pg_stat_statements_reset_aux()</function> independently of whole
+ statistics reset. Auxiliary statistics makes it possible to get accurate
+ min/max statistic values for sampling solutions avoiding overall reset. This
+ view contains one row for each distinct combination of database ID, user ID,
+ query ID and whether it's a top-level statement or not (up to the maximum
+ number of distinct statements that the module can track). The columns of the
+ view are shown in <xref linkend="pgstatstatementsaux-columns"/>.
+ </para>
+
+ <table id="pgstatstatementsaux-columns">
+ <title><structname>pg_stat_statements_aux</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>userid</structfield> <type>oid</type>
+ (references <link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.<structfield>oid</structfield>)
+ </para>
+ <para>
+ OID of user who executed the statement
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>dbid</structfield> <type>oid</type>
+ (references <link linkend="catalog-pg-database"><structname>pg_database</structname></link>.<structfield>oid</structfield>)
+ </para>
+ <para>
+ OID of database in which the statement was executed
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>toplevel</structfield> <type>bool</type>
+ </para>
+ <para>
+ True if the query was executed as a top-level statement
+ (always true if <varname>pg_stat_statements.track</varname> is set to
+ <literal>top</literal>)
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>queryid</structfield> <type>bigint</type>
+ </para>
+ <para>
+ Hash code to identify identical normalized queries.
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>query</structfield> <type>text</type>
+ </para>
+ <para>
+ Text of a representative statement
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>aux_min_plan_time</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Minimum time spent planning the statement since
+ <structfield>aux_stats_since</structfield>, in milliseconds
+ (if <varname>pg_stat_statements.track_planning</varname> is enabled,
+ otherwise zero)
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>aux_max_plan_time</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Maximum time spent planning the statement since
+ <structfield>aux_stats_since</structfield>, in milliseconds
+ (if <varname>pg_stat_statements.track_planning</varname> is enabled,
+ otherwise zero)
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>aux_min_exec_time</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Minimum time spent executing the statement since
+ <structfield>aux_stats_since</structfield>, in milliseconds
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>aux_max_exec_time</structfield> <type>double precision</type>
+ </para>
+ <para>
+ Maximum time spent executing the statement since
+ <structfield>aux_stats_since</structfield>, 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>
+ Timestamp of statistics gathering start for the statement
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>aux_stats_since</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of auxiliary statistics gathering start for the statement. In
+ case of a new statement entry value of this field is the same as
+ <structfield>stats_since</structfield>, but after the auxiliary
+ statistics reset this field holds the timestamp of a reset
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+
<sect2>
<title>The <structname>pg_stat_statements_info</structname> View</title>
@@ -595,6 +754,27 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <function>pg_stat_statements_reset_aux(userid Oid, dbid Oid, queryid bigint) returns void</function>
+ <indexterm>
+ <primary>pg_stat_statements_reset_aux</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ <function>pg_stat_statements_reset_aux</function> function discards only
+ the auxiliary statistics available in
+ <structname>pg_stat_statements_aux</structname> view. This function will
+ update the value of <structfield>aux_stats_since</structfield> with the
+ current timestamp. By default, this function can only be executed by
+ superusers.
+ Access may be granted to others using <command>GRANT</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<function>pg_stat_statements(showtext boolean) returns setof record</function>
--
2.30.2
Hi, Andrey!
I've checked the 5th version of the patch and there are some remarks.
I've created a new view named pg_stat_statements_aux. But for now both
views are using the same function pg_stat_statements which returns all
fields. It seems reasonable to me - if sampling solution will need all
values it can query the function.
Agreed, it might be useful in some cases.
But it seems "stats_reset" term is not quite correct in this case. The
"stats_since" field holds the timestamp of hashtable entry, but not the
reset time. The same applies to aux_stats_since - for new statement it
holds its entry time, but in case of reset it will hold the reset time.
Thanks for the clarification. Indeed if we mean the word 'reset' as the
removal of all the hashtable entries during pg_stat_statements_reset()
then we shouldn't use it for the first occurrence timestamp in the
struct pgssEntry.
So with the stats_since field everything is clear.
On the other hand aux_stats_since field can be updated for two reasons:
1) The same as for stats_since due to first occurrence of entry in the
hashtable. And it will be equal to stats_since timestamp in that case.
2) Due to an external reset from an upper level sampler.
I think it's not very important how to name this field, but it would be
better to mention both these reasons in the comment.
As for more important things, if the aux_min_time initial value is zero
like now, then if condition on line 1385 of pg_stat_statements.c will
never be true and aux_min_time will remain zero. Init aux_min_time with
INT_MAX can solve this problem.
It is possible to reduce size of entry_reset_aux() function via
removing if condition on line 2606 and entire third branch from line
2626. Thanks to check in 2612 this will work in all cases.
Also it would be nice to move the repeating several times
lines 2582-2588 into separate function. I think this can make
entry_reset_aux() more shorter and clearer.
In general, the 5th patch applies with no problems, make check-world and
CI gives no error and patch seems to be closely to become RFC.
With best regards,
--
Anton A. Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi,
On Mon, Jan 24, 2022 at 08:16:06PM +0300, Anton A. Melnikov wrote:
Hi, Andrey!
I've checked the 5th version of the patch and there are some remarks.
I've created a new view named pg_stat_statements_aux. But for now both
views are using the same function pg_stat_statements which returns all
fields. It seems reasonable to me - if sampling solution will need all
values it can query the function.Agreed, it might be useful in some cases.
But it seems "stats_reset" term is not quite correct in this case. The
"stats_since" field holds the timestamp of hashtable entry, but not the
reset time. The same applies to aux_stats_since - for new statement it
holds its entry time, but in case of reset it will hold the reset time.Thanks for the clarification. Indeed if we mean the word 'reset' as the
removal of all the hashtable entries during pg_stat_statements_reset() then
we shouldn't use it for the first occurrence timestamp in the struct
pgssEntry.
So with the stats_since field everything is clear.
On the other hand aux_stats_since field can be updated for two reasons:
1) The same as for stats_since due to first occurrence of entry in the
hashtable. And it will be equal to stats_since timestamp in that case.
2) Due to an external reset from an upper level sampler.
I think it's not very important how to name this field, but it would be
better to mention both these reasons in the comment.
Are those 4 new counters really worth it?
The min/max were added to make pg_stat_statements a bit more useful if you
have nothing else using that extension. But once you setup a tool that
snapshots the metrics regularly, do you really need to know e.g. "what was the
maximum execution time in the last 3 years", which will typically be what
people will retrieve from the non-aux min/max counters, rather than simply
using your additional tool for better and more precise information?
Hi Julien,
Tue, 2022-01-25 at 18:08 +0800, Julien Rouhaud wrote:
Are those 4 new counters really worth it?
The min/max were added to make pg_stat_statements a bit more useful
if you
have nothing else using that extension. But once you setup a tool
that
snapshots the metrics regularly, do you really need to know e.g.
"what was the
maximum execution time in the last 3 years", which will typically be
what
people will retrieve from the non-aux min/max counters, rather than
simply
using your additional tool for better and more precise information?
Of course we can replace old min/max metrics with the new "aux" min/max
metrics. It seems reasonable to me because they will have the same
behavior until we touch reset_aux. I think we can assume that min/max
data is saved somewhere if reset_aux was performed, but how about the
backward compatibility?
There may be some monitoring solutions that doesn't expect min/max
stats reset independently of other statement statistics.
It seems highly unlikely to me, because the min/max stats for "the last
3 years" is obvious unusable but maybe someone uses them as a sign of
something?
Are we need to worry about that?
Also, there is one more dramatic consequence of such decision...
What min/max values should be returned after the auxiliary reset but
before the next statement execution?
The NULL values seems reasonable but there was not any NULLs before and
backward compatibility seems broken. Another approach is to return the
old values of min/max stats and the old aux_stats_since value until the
next statement execution but it seems strange when the reset was
performed and it doesn't affected any stats instantly.
regards,
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi Andrei,
On Tue, Jan 25, 2022 at 02:58:17PM +0300, Andrei Zubkov wrote:
Of course we can replace old min/max metrics with the new "aux" min/max
metrics. It seems reasonable to me because they will have the same
behavior until we touch reset_aux. I think we can assume that min/max
data is saved somewhere if reset_aux was performed, but how about the
backward compatibility?
There may be some monitoring solutions that doesn't expect min/max
stats reset independently of other statement statistics.
It seems highly unlikely to me, because the min/max stats for "the last
3 years" is obvious unusable but maybe someone uses them as a sign of
something?
To be honest I don't see how any monitoring solution could make any use of
those fields as-is. For that reason in PoWA we unfortunately have to entirely
ignore them. There was a previous attempt to provide a way to reset those
counters only (see [1]/messages/by-id/1762890.8ARNpCrDLI@peanuts2), but it was returned with feedback due to lack of TLC
from the author.
Are we need to worry about that?
I don't think it's a problem, as once you have a solution on top of
pg_stat_statements, you get information order of magnitude better from that
solution instead of pg_stat_statements. And if that's a problem, well either
don't reset those counters, or don't use the external solution if it does it
automatically and you're not ok with it.
Also, there is one more dramatic consequence of such decision...
What min/max values should be returned after the auxiliary reset but
before the next statement execution?
The NULL values seems reasonable but there was not any NULLs before and
backward compatibility seems broken. Another approach is to return the
old values of min/max stats and the old aux_stats_since value until the
next statement execution but it seems strange when the reset was
performed and it doesn't affected any stats instantly.
If you're worried about some external table having a NOT NULL constraint for
those fields, how about returning NaN instead? That's a valid value for a
double precision data type.
Hi Julien,
On Tue, 2022-01-25 at 20:22 +0800, Julien Rouhaud wrote:
To be honest I don't see how any monitoring solution could make any
use of
those fields as-is. For that reason in PoWA we unfortunately have to
entirely
ignore them. There was a previous attempt to provide a way to reset
those
counters only (see [1]), but it was returned with feedback due to
lack of TLC
from the author.
Thank you, I've just seen a thread in [1], it was of course a weak
attempt and I think it could be done better.
I don't think it's a problem, as once you have a solution on top of
pg_stat_statements, you get information order of magnitude better
from that solution instead of pg_stat_statements.
Agreed. I'm worry about having different solutions running
simultaneously. All of them may want to get accurate min/max values,
but if they all will reset min/max statistics, they will interfere each
other. It seems that min/max resetting should be configurable in each
sampler as a partial problem solution. At least, every sampling
solution can make a decision based on reset timestamps provided by
pg_stat_statements now.
If you're worried about some external table having a NOT NULL
constraint for
those fields, how about returning NaN instead? That's a valid value
for a
double precision data type.
I don't know for sure what we can expect to be wrong here. My opinion
is to use NULLs, as they seems more suitable here. But this can be done
only if we are not expecting significant side effects.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hello!
On 26.01.2022 16:43, Andrei Zubkov wrote:
If you're worried about some external table having a NOT NULL
constraint for
those fields, how about returning NaN instead? That's a valid value
for a
double precision data type.I don't know for sure what we can expect to be wrong here. My opinion
is to use NULLs, as they seems more suitable here. But this can be done
only if we are not expecting significant side effects.
Let me suggest for your consideration an additional reset request flag
that can be used to synchronize reset in a way similar to interrupt
handling.
External reset can set this flag immediately. Then pg_stat_statements
will wait for the moment when the required query hits into the
statistics and only at this moment really reset the aux statistics,
write a new timestamp and clear the flag. At the time of real reset,
total_time will be determined, and pg_stat_statements can immediately
initialize min and max correctly.
From reset to the next query execution the aux view will give old
correct values so neither NaNs nor NULLs will be required.
Also we can put the value of reset request flag into the aux view to
give feedback to the external application that reset was requested.
With best regards,
--
Anton A. Melnikov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
This patch seems to have gotten some feedback but development has
stalled. It's marked "waiting on author" but I'm not clear exactly
what is expected from the authors here. It seems there isn't really
consensus on the design at the moment. There's been no emails in over
a month.
Fwiw I find the idea of having a separate "aux" table kind of awkward.
It'll seem strange to users not familiar with the history and without
any clear idea why the fields are split.
Hi
On Fri, 2022-03-25 at 00:37 -0400, Greg Stark wrote:
Fwiw I find the idea of having a separate "aux" table kind of
awkward.
It'll seem strange to users not familiar with the history and without
any clear idea why the fields are split.
Greg, thank you for your attention and for your thought.
I've just completed the 6th version of a patch implementing idea
proposed by Julien Rouhaud, i.e. without auxiliary statistics. 6th
version will reset current min/max fields to zeros until the first plan
or execute. I've decided to use zeros here because planning statistics
is zero in case of disabled tracking. I think sampling solution could
easily handle this.
--
Regards, Andrei Zubkov
Attachments:
v6-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v6-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 68cd5efee7b3dbdb1b4034ab4c47249a23ca9d04 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 25 Mar 2022 12:30:03 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new function
pg_stat_statements_aux_reset(userid oid, dbid oid, queryid bigint).
Timestamp of such reset is stored in the minmax_stats_since field for
each statement.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/pg_stat_statements.out | 140 +++++++++++++
.../pg_stat_statements--1.9--1.10.sql | 104 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 197 ++++++++++++++++--
.../pg_stat_statements.control | 2 +-
.../sql/pg_stat_statements.sql | 92 ++++++++
doc/src/sgml/pgstatstatements.sgml | 64 +++++-
7 files changed, 580 insertions(+), 22 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38..edc40c8bbf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6..d59fcdc403 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,144 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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_minmax_reset(0, 0, queryid)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+ pg_stat_statements_minmax_reset
+---------------------------------
+
+(1 row)
+
+-- 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)
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_minmax_reset();
+ pg_stat_statements_minmax_reset
+---------------------------------
+
+(1 row)
+
+-- 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_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 | 2 | 2 | 2 | 0
+(1 row)
+
+-- Execute first query once more to check stats update
+SELECT 1 AS "STMTTS1";
+ STMTTS1
+---------
+ 1
+(1 row)
+
+-- check
+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_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_exec_zero | minmax_stats_since_after_ref | stats_since_after_ref
+-------+------------------+------------------------------+-----------------------
+ 2 | 1 | 2 | 0
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 0000000000..b068f20cc6
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,104 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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 VIEW pg_stat_statements;
+DROP FUNCTION pg_stat_statements(boolean);
+
+/* Now redefine */
+CREATE FUNCTION pg_stat_statements(IN showtext boolean,
+ OUT userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+CREATE FUNCTION pg_stat_statements_minmax_reset(IN userid Oid DEFAULT 0,
+ IN dbid Oid DEFAULT 0,
+ IN queryid bigint DEFAULT 0
+)
+RETURNS void
+AS 'MODULE_PATHNAME', 'pg_stat_statements_minmax_reset_1_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 9e525a6ad3..280512957f 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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 moment */
+ TimestampTz minmax_stats_since; /* timestamp of last min/max values reset */
+ slock_t mutex; /* protects the counters only */
} pgssEntry;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_minmax_reset_1_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,6 +350,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_minmax_reset(Oid userid, Oid dbid, uint64 queryid);
static void entry_reset(Oid userid, Oid dbid, uint64 queryid);
static char *generate_normalized_query(JumbleState *jstate, const char *query,
int query_loc, int *query_len_p);
@@ -649,6 +655,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 */
@@ -1350,11 +1358,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 min/max statistics reset was happen
+ */
+ 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;
@@ -1385,6 +1405,25 @@ done:
pfree(norm_query);
}
+/*
+ * Reset min/max statement statistics corresponding to userid, dbid, and queryid.
+ */
+Datum
+pg_stat_statements_minmax_reset_1_10(PG_FUNCTION_ARGS)
+{
+ Oid userid;
+ Oid dbid;
+ uint64 queryid;
+
+ userid = PG_GETARG_OID(0);
+ dbid = PG_GETARG_OID(1);
+ queryid = (uint64) PG_GETARG_INT64(2);
+
+ entry_minmax_reset(userid, dbid, queryid);
+
+ PG_RETURN_VOID();
+}
+
/*
* Reset statement statistics corresponding to userid, dbid, and queryid.
*/
@@ -1422,7 +1461,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1474,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1597,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1679,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));
@@ -1693,6 +1749,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);
}
@@ -1727,6 +1785,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
else
stddev = 0.0;
values[i++] = Float8GetDatumFast(stddev);
+
}
}
values[i++] = Int64GetDatumFast(tmp.rows);
@@ -1764,6 +1823,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1835,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1949,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;
@@ -2430,6 +2497,106 @@ gc_fail:
record_gc_qtexts();
}
+/*
+ * Reset min/max values of specified entries
+ */
+static void
+entry_minmax_reset(Oid userid, Oid dbid, uint64 queryid)
+{
+ HASH_SEQ_STATUS hash_seq;
+ pgssEntry *entry;
+ Counters *entry_counters;
+ pgssHashKey key;
+ TimestampTz minmax_stats_reset;
+
+ if (!pgss || !pgss_hash)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("pg_stat_statements must be loaded via shared_preload_libraries")));
+
+ LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
+
+ minmax_stats_reset = GetCurrentTimestamp();
+
+ if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
+ {
+ /* If all the parameters are available, use the fast path. */
+ memset(&key, 0, sizeof(pgssHashKey));
+ key.userid = userid;
+ key.dbid = dbid;
+ key.queryid = queryid;
+
+ /*
+ * Reset min/max values, starting with the nested-level entry
+ * For min/max values reset sign is min = 0 and max = 0
+ */
+ key.toplevel = false;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+
+ /* Reset min/max values for top level statements */
+ key.toplevel = true;
+
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
+ if (entry) /* found */
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ }
+ else if (userid != 0 || dbid != 0 || queryid != UINT64CONST(0))
+ {
+ /* Reset min/max values for entries corresponding to valid parameters. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ if ((!userid || entry->key.userid == userid) &&
+ (!dbid || entry->key.dbid == dbid) &&
+ (!queryid || entry->key.queryid == queryid))
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ }
+ }
+ else
+ {
+ /* Reset min/max values for all entries. */
+ hash_seq_init(&hash_seq, pgss_hash);
+ while ((entry = hash_seq_search(&hash_seq)) != NULL)
+ {
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ }
+
+ LWLockRelease(pgss->lock);
+}
+
/*
* Release entries corresponding to parameters passed.
*/
@@ -2459,7 +2626,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Remove the key if it exists, starting with the nested-level entry */
key.toplevel = false;
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
if (entry) /* found */
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed50..0747e48138 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c18..e5bab74d0a 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,96 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+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_minmax_reset(0, 0, queryid)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+
+-- 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%';
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_minmax_reset();
+
+-- 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_stats_since_after_ref,
+ 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
+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_stats_since_after_ref,
+ count(*) FILTER (
+ WHERE stats_since >= :'ref_ts'
+ ) as stats_since_after_ref
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index bc9d5bdbe3..91ee49aa1e 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -30,7 +30,8 @@
these statistics, the module provides views
<structname>pg_stat_statements</structname> and
<structname>pg_stat_statements_info</structname>,
- and the utility functions <function>pg_stat_statements_reset</function> and
+ and the utility functions <function>pg_stat_statements_reset</function>,
+ <function>pg_stat_statements_minmax_reset</function> and
<function>pg_stat_statements</function>. These are not available globally but
can be enabled for a specific database with
<command>CREATE EXTENSION pg_stat_statements</command>.
@@ -142,7 +143,9 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist time after reset performed by the
+ <function>pg_stat_statements_minmax_reset</function> function
</para></entry>
</row>
@@ -153,7 +156,9 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist time after reset performed by the
+ <function>pg_stat_statements_minmax_reset</function> function
</para></entry>
</row>
@@ -203,7 +208,10 @@
<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 contain zero until this statement
+ is executed fist time after reset performed by the
+ <function>pg_stat_statements_minmax_reset</function> function
</para></entry>
</row>
@@ -212,7 +220,10 @@
<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 contain zero until this statement
+ is executed fist time after reset performed by the
+ <function>pg_stat_statements_minmax_reset</function> function
</para></entry>
</row>
@@ -379,6 +390,24 @@
Total amount of WAL generated by the statement in bytes
</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>
+ Timestamp of statistics gathering start for the 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>
+ Timestamp of min/max fields statistics gathering start for the statement
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -595,6 +624,31 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <function>pg_stat_statements_minmax_reset(userid Oid, dbid Oid, queryid bigint) returns void</function>
+ <indexterm>
+ <primary>pg_stat_statements_minmax_reset</primary>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ <function>pg_stat_statements_minmax_reset</function> discards only the values of
+ minimun and maximum execution and planning time (i.e.
+ <structfield>min_plan_time</structfield>, <structfield>max_plan_time</structfield>, <structfield>min_exec_time</structfield> and <structfield>max_exec_time</structfield>
+ fields) gathered so far by <filename>pg_stat_statements</filename> corresponding
+ to the specified <structfield>userid</structfield>, <structfield>dbid</structfield>
+ and <structfield>queryid</structfield> (like <function>pg_stat_statements_reset</function>
+ function). This function will update the value of
+ <structfield>minmax_stats_since</structfield> field with the current
+ timestamp.
+ By default, this function can only be executed by superusers.
+ Access may be granted to others using <command>GRANT</command>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
<function>pg_stat_statements(showtext boolean) returns setof record</function>
--
2.31.1
Hi,
On Fri, Mar 25, 2022 at 01:25:23PM +0300, Andrei Zubkov wrote:
Greg, thank you for your attention and for your thought.
I've just completed the 6th version of a patch implementing idea
proposed by Julien Rouhaud, i.e. without auxiliary statistics. 6th
version will reset current min/max fields to zeros until the first plan
or execute.
Thanks!
I've decided to use zeros here because planning statistics
is zero in case of disabled tracking. I think sampling solution could
easily handle this.
I'm fine with it. It's also consistent with the planning counters when
track_planning is disabled. And even if the sampling solution doesn't handle
it, you will simply get consistent values, like "0 calls with minmax timing of
0 msec", so it's not really a problem.
Feature wise, I'm happy with the patch. I just have a few comments.
Tests:
- it's missing some test in sql/oldextversions.sql to validate that the code
works with the extension in version 1.9
- the last test removed the minmax_plan_zero field, why?
Code:
+ TimestampTz stats_since; /* timestamp of entry allocation moment */
I think "timestamp of entry allocation" is enough?
+ * Calculate min and max time. min = 0 and max = 0
+ * means that min/max statistics reset was happen
maybe "means that the min/max statistics were reset"
+/*
+ * Reset min/max values of specified entries
+ */
+static void
+entry_minmax_reset(Oid userid, Oid dbid, uint64 queryid)
+{
[...]
There's a lot of duplicated logic with entry_reset().
Would it be possible to merge at least the C reset function to handle either
all-metrics or minmax-only? Also, maybe it would be better to have a single SQL
reset function, something like:
pg_stat_statements_reset(IN userid Oid DEFAULT 0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0,
IN minmax_only DEFAULT false
)
Doc:
+ <structfield>stats_since</structfield> <type>timestamp with time zone</type>
+ </para>
+ <para>
+ Timestamp of statistics gathering start for the statement
The description is a bit weird. Maybe like "Time at which statistics gathering
started for this statement"? Same for the minmax version.
Hi Julien!
Thank you for such detailed review!
On Wed, 2022-03-30 at 17:31 +0800, Julien Rouhaud wrote:
Feature wise, I'm happy with the patch. I just have a few comments.
Tests:
- it's missing some test in sql/oldextversions.sql to validate that the
code
works with the extension in version 1.9
Yes, I've just added some tests there, but it seems they are not quite
suficient. Maybe we should try to do some queries to views and
functions in old versions? A least when new C function version
appears...
During tests developing I've noted that current test of
pg_stat_statements_info view actually tests only view access. However
we can test at least functionality of stats_reset field like this:
SELECT now() AS ref_ts \gset
SELECT dealloc, stats_reset >= :'ref_ts' FROM pg_stat_statements_info;
SELECT pg_stat_statements_reset();
SELECT dealloc, stats_reset >= :'ref_ts' FROM pg_stat_statements_info;
Does it seems reasonable?
- the last test removed the minmax_plan_zero field, why?
My thaught was as follows... Reexecution of the same query will
definitely cause execution. However, most likely it wouldn't be
planned, but if it would (maybe this is possible, or maybe it will be
possible in the future in some cases) the test shouldn't fail. Checking
of only execution stats seems enough to me - in most cases we can't
check planning stats with such test anyway.
What do you think about it?
Code:
+ TimestampTz stats_since; /* timestamp of entry
allocation moment */I think "timestamp of entry allocation" is enough?
Yes
+ * Calculate min and max time. min = 0 and max = 0 + * means that min/max statistics reset was happenmaybe "means that the min/max statistics were reset"
Agreed
+/* + * Reset min/max values of specified entries + */ +static void +entry_minmax_reset(Oid userid, Oid dbid, uint64 queryid) +{ [...]There's a lot of duplicated logic with entry_reset().
Would it be possible to merge at least the C reset function to handle
either
all-metrics or minmax-only?
Great point! I've merged minmax reset functionality in the entry_reset
function.
Also, maybe it would be better to have a single SQL
reset function, something like:pg_stat_statements_reset(IN userid Oid DEFAULT 0,
IN dbid Oid DEFAULT 0,
IN queryid bigint DEFAULT 0,
IN minmax_only DEFAULT false
)
Of course!
Doc:
+ <structfield>stats_since</structfield> <type>timestamp with time zone</type> + </para> + <para> + Timestamp of statistics gathering start for the statementThe description is a bit weird. Maybe like "Time at which statistics
gathering
started for this statement"? Same for the minmax version.
Agreed.
I've attached 7th patch version with fixes mentioned above.
--
Best regards, Andrei Zubkov
Attachments:
v7-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v7-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 41e56237b2c4b68ff101590e6f7c4cdc79b53816 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 31 Mar 2022 12:37:17 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 ++++++
.../expected/pg_stat_statements.out | 140 ++++++++++++
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++++++
.../pg_stat_statements/pg_stat_statements.c | 200 +++++++++++++++---
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 92 ++++++++
doc/src/sgml/pgstatstatements.sgml | 52 ++++-
9 files changed, 625 insertions(+), 41 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..5d0285853f6 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,144 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+-- 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)
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_reset(0, 0, 0, true);
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+-- 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_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 | 2 | 2 | 2 | 0
+(1 row)
+
+-- Execute first query once more to check stats update
+SELECT 1 AS "STMTTS1";
+ STMTTS1
+---------
+ 1
+(1 row)
+
+-- check
+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_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_exec_zero | minmax_stats_since_after_ref | stats_since_after_ref
+-------+------------------+------------------------------+-----------------------
+ 2 | 1 | 2 | 0
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..2f9cc0944e7
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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 void
+AS 'MODULE_PATHNAME', 'pg_stat_statements_reset_1_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..879f220066c 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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 void 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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,7 +1418,25 @@ 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_10(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);
+
+ entry_reset(userid, dbid, queryid, minmax_only);
PG_RETURN_VOID();
}
@@ -1410,7 +1447,7 @@ pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
Datum
pg_stat_statements_reset(PG_FUNCTION_ARGS)
{
- entry_reset(0, 0, 0);
+ entry_reset(0, 0, 0, false);
PG_RETURN_VOID();
}
@@ -1422,7 +1459,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1472,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1595,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1677,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));
@@ -1693,6 +1747,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);
}
@@ -1764,6 +1820,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1832,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1946,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;
@@ -2431,17 +2495,19 @@ gc_fail:
}
/*
- * Release entries corresponding to parameters passed.
+ * Reset entries corresponding to parameters passed.
*/
static void
-entry_reset(Oid userid, Oid dbid, uint64 queryid)
+entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
+ Counters *entry_counters;
FILE *qfile;
long num_entries;
long num_remove = 0;
pgssHashKey key;
+ TimestampTz minmax_stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
@@ -2451,6 +2517,10 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
num_entries = hash_get_num_entries(pgss_hash);
+ if (minmax_only) {
+ minmax_stats_reset = GetCurrentTimestamp();
+ }
+
if (userid != 0 && dbid != 0 && queryid != UINT64CONST(0))
{
/* If all the parameters are available, use the fast path. */
@@ -2459,23 +2529,57 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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);
+
+ if (entry) {
+ /* Found */
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
+ }
- /* Also remove entries for top level statements */
+ /* Reset entries for top level statements */
key.toplevel = true;
-
- /* Remove the key if exists */
- 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);
+
+ if (entry) {
+ /* Found */
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
+ }
}
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)
{
@@ -2483,8 +2587,22 @@ 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++;
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
}
}
}
@@ -2494,8 +2612,22 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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++;
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = minmax_stats_reset;
+ }
+ else
+ {
+ /* Remove entry otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
}
}
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..af41243d4e3 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,96 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+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)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+
+-- 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%';
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_reset(0, 0, 0, true);
+
+-- 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_stats_since_after_ref,
+ 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
+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_stats_since_after_ref,
+ count(*) FILTER (
+ WHERE stats_since >= :'ref_ts'
+ ) as stats_since_after_ref
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..bad50031fb5 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -142,7 +142,10 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -153,7 +156,10 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -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 contain zero until this statement
+ is executed fist 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 contain zero until this statement
+ is executed fist 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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 void</function>
<indexterm>
<primary>pg_stat_statements_reset</primary>
</indexterm>
@@ -589,6 +623,14 @@
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 execution and planning 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.
By default, this function can only be executed by superusers.
Access may be granted to others using <command>GRANT</command>.
</para>
--
2.31.1
FYI this has a compiler warning showing up on the cfbot:
[13:19:51.544] pg_stat_statements.c: In function ‘entry_reset’:
[13:19:51.544] pg_stat_statements.c:2598:32: error:
‘minmax_stats_reset’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
[13:19:51.544] 2598 | entry->minmax_stats_since = minmax_stats_reset;
[13:19:51.544] | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
If the patch is otherwise ready to commit then this is an issue that
should be fixed before marking it ready to commit.
Given that this is the last week before feature freeze it'll probably
get moved to a future commitfest unless it's ready to commit.
Hi,
On Fri, Apr 01, 2022 at 11:38:52AM -0400, Greg Stark wrote:
FYI this has a compiler warning showing up on the cfbot:
[13:19:51.544] pg_stat_statements.c: In function ‘entry_reset’:
[13:19:51.544] pg_stat_statements.c:2598:32: error:
‘minmax_stats_reset’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
[13:19:51.544] 2598 | entry->minmax_stats_since = minmax_stats_reset;
[13:19:51.544] | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~If the patch is otherwise ready to commit then this is an issue that
should be fixed before marking it ready to commit.Given that this is the last week before feature freeze it'll probably
get moved to a future commitfest unless it's ready to commit.
As I mentioned in my last review I think feature wise the patch is ok, it just
needed a few minor changes. It's a small patch but can help *a lot* tools on
top of pg_stat_statements and give users a better overview of their workload so
it would be nice to commit it in v15.
I was busy looking at the prefetch patch today (not done yet), but I plan to
review the last version over the weekend. After a quick look at the patch it
seems like a compiler bug. I'm not sure which clang version is used, but can't
reproduce it locally using clang 13. I already saw similar false positive,
when a variable is initialized in a branch (here minmax_only == true), and only
then used in similar branches. I guess that pg_stat_statement_reset() is so
expensive that an extra gettimeofday() wouldn't change much. Otherwise
initializing to NULL should be enough.
Hi,
Thank you, Greg
On Fri, 2022-04-01 at 11:38 -0400, Greg Stark wrote:
[13:19:51.544] pg_stat_statements.c: In function ‘entry_reset’:
[13:19:51.544] pg_stat_statements.c:2598:32: error:
‘minmax_stats_reset’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
[13:19:51.544] 2598 | entry->minmax_stats_since = minmax_stats_reset;
[13:19:51.544] | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
I was afraid of such warning can appear..
On Sat, 2022-04-02 at 00:13 +0800, Julien Rouhaud wrote:
I guess that pg_stat_statement_reset() is so
expensive that an extra gettimeofday() wouldn't change much.
Agreed
Otherwise
initializing to NULL should be enough.
Julien, I would prefer an extra GetCurrentTimestamp(). So, I've opted
to use the common unconditional
stats_reset = GetCurrentTimestamp();
for an entire entry_reset function due to the following:
1. It will be uniform for stats_reset and minmax_stats_reset
2. As you mentioned, it wouldn't change a much
3. The most common way to use this function is to reset all statements
meaning that GetCurrentTimestamp() will be called anyway to update the
value of stats_reset field in pg_stat_statements_info view
4. Actually I would like that pg_stat_statements_reset function was
able to return the value of stats_reset as its result. This could give
to the sampling solutions the ability to check if the last reset (of
any type) was performed by this solution or any other reset was
performed by someone else. It seems valuable to me, but it changes the
result type of the pg_stat_statements_reset() function, so I don't know
if we can do that right now.
v8 attached
--
regards, Andrei
Attachments:
v8-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v8-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From a905dcbd8ca891a1aaf9652324650b87cdcae001 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 1 Apr 2022 22:09:49 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 ++++++
.../expected/pg_stat_statements.out | 140 ++++++++++++
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++++++
.../pg_stat_statements/pg_stat_statements.c | 199 +++++++++++++++---
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 92 ++++++++
doc/src/sgml/pgstatstatements.sgml | 52 ++++-
9 files changed, 623 insertions(+), 42 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..5d0285853f6 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1077,4 +1077,144 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+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)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+-- 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)
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_reset(0, 0, 0, true);
+ pg_stat_statements_reset
+--------------------------
+
+(1 row)
+
+-- 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_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 | 2 | 2 | 2 | 0
+(1 row)
+
+-- Execute first query once more to check stats update
+SELECT 1 AS "STMTTS1";
+ STMTTS1
+---------
+ 1
+(1 row)
+
+-- check
+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_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_exec_zero | minmax_stats_since_after_ref | stats_since_after_ref
+-------+------------------+------------------------------+-----------------------
+ 2 | 1 | 2 | 0
+(1 row)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..2f9cc0944e7
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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 void
+AS 'MODULE_PATHNAME', 'pg_stat_statements_reset_1_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..277b24e1bd0 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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 void 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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,7 +1418,25 @@ 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_10(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);
+
+ entry_reset(userid, dbid, queryid, minmax_only);
PG_RETURN_VOID();
}
@@ -1410,7 +1447,7 @@ pg_stat_statements_reset_1_7(PG_FUNCTION_ARGS)
Datum
pg_stat_statements_reset(PG_FUNCTION_ARGS)
{
- entry_reset(0, 0, 0);
+ entry_reset(0, 0, 0, false);
PG_RETURN_VOID();
}
@@ -1422,7 +1459,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1472,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1595,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1677,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));
@@ -1693,6 +1747,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);
}
@@ -1764,6 +1820,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1832,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1946,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;
@@ -2431,17 +2495,19 @@ gc_fail:
}
/*
- * Release entries corresponding to parameters passed.
+ * Reset entries corresponding to parameters passed.
*/
static void
-entry_reset(Oid userid, Oid dbid, uint64 queryid)
+entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
+ Counters *entry_counters;
FILE *qfile;
long num_entries;
long num_remove = 0;
pgssHashKey key;
+ TimestampTz stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
@@ -2451,6 +2517,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. */
@@ -2459,23 +2527,57 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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);
+
+ if (entry) {
+ /* Found */
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
+ }
- /* Also remove entries for top level statements */
+ /* Reset entries for top level statements */
key.toplevel = true;
-
- /* Remove the key if exists */
- 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);
+
+ if (entry) {
+ /* Found */
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
+ }
}
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)
{
@@ -2483,8 +2585,22 @@ 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++;
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
}
}
}
@@ -2494,8 +2610,22 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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++;
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = stats_reset;
+ }
+ else
+ {
+ /* Remove entry otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ num_remove++;
+ }
}
}
@@ -2509,7 +2639,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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..af41243d4e3 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -442,4 +442,96 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset();
+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)
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS1%';
+
+-- 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%';
+
+-- Perform common min/max reset
+SELECT pg_stat_statements_reset(0, 0, 0, true);
+
+-- 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_stats_since_after_ref,
+ 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
+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_stats_since_after_ref,
+ count(*) FILTER (
+ WHERE stats_since >= :'ref_ts'
+ ) as stats_since_after_ref
+FROM pg_stat_statements
+WHERE query LIKE '%STMTTS%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..bad50031fb5 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -142,7 +142,10 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -153,7 +156,10 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -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 contain zero until this statement
+ is executed fist 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 contain zero until this statement
+ is executed fist 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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 void</function>
<indexterm>
<primary>pg_stat_statements_reset</primary>
</indexterm>
@@ -589,6 +623,14 @@
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 execution and planning 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.
By default, this function can only be executed by superusers.
Access may be granted to others using <command>GRANT</command>.
</para>
--
2.31.1
Hi,
On 2022-04-01 22:47:02 +0300, Andrei Zubkov wrote:
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL); + + if (entry) { + /* Found */ + if (minmax_only) { + /* When requested reset only min/max statistics of an entry */ + entry_counters = &entry->counters; + for (int kind = 0; kind < PGSS_NUMKIND; kind++) + { + entry_counters->max_time[kind] = 0; + entry_counters->min_time[kind] = 0; + } + entry->minmax_stats_since = stats_reset; + } + else + { + /* Remove the key otherwise */ + hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL); + num_remove++; + } + }
It seems decidedly not great to have four copies of this code. It was already
not great before, but this patch makes the duplicated section go from four
lines to 20 or so.
Greetings,
Andres Freund
On Thu, Mar 31, 2022 at 01:06:10PM +0300, Andrei Zubkov wrote:
On Wed, 2022-03-30 at 17:31 +0800, Julien Rouhaud wrote:
Feature wise, I'm happy with the patch.� I just have a few comments.
Tests:
- it's missing some test in sql/oldextversions.sql to validate that the
code
� works with the extension in version 1.9Yes, I've just added some tests there, but it seems they are not quite
suficient. Maybe we should try to do some queries to views and
functions in old versions? A least when new C function version
appears...
I'm not sure if that's really helpful. If you have new C functions and old
SQL-version, you won't be able to reach them anyway. Similarly, if you have
the new SQL but the old .so (which we can't test), it will just fail as the
symbol doesn't exist. The real problem that has to be explicitly handled by
the C code is different signatures for C functions.
During tests developing I've noted that current test of
pg_stat_statements_info view actually tests only view access. However
we can test at least functionality of stats_reset field like this:SELECT now() AS ref_ts \gset
SELECT dealloc, stats_reset >= :'ref_ts' FROM pg_stat_statements_info;
SELECT pg_stat_statements_reset();
SELECT dealloc, stats_reset >= :'ref_ts' FROM pg_stat_statements_info;Does it seems reasonable?
It looks reasonable, especially if the patch adds a new mode for the reset
function.
- the last test removed the minmax_plan_zero field, why?
My thaught was as follows... Reexecution of the same query will
definitely cause execution. However, most likely it wouldn't be
planned, but if it would (maybe this is possible, or maybe it will be
possible in the future in some cases) the test shouldn't fail. Checking
of only execution stats seems enough to me - in most cases we can't
check planning stats with such test anyway.
What do you think about it?
Ah I see. I guess we could set plan_cache_mode to force_generic_plan to make
sure we go though planning. But otherwise just adding a comment saying that
the test has to be compatible with different plan caching approach would be
fine with me.
Thanks for the work on merging the functions! I will reply on the other parts
of the thread where some discussion started.
On Fri, Apr 01, 2022 at 10:47:02PM +0300, Andrei Zubkov wrote:
On Fri, 2022-04-01 at 11:38 -0400, Greg Stark wrote:
[13:19:51.544] pg_stat_statements.c: In function ‘entry_reset’:
[13:19:51.544] pg_stat_statements.c:2598:32: error:
‘minmax_stats_reset’ may be used uninitialized in this function
[-Werror=maybe-uninitialized]
[13:19:51.544] 2598 | entry->minmax_stats_since = minmax_stats_reset;
[13:19:51.544] | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~I was afraid of such warning can appear..
On Sat, 2022-04-02 at 00:13 +0800, Julien Rouhaud wrote:
I guess that pg_stat_statement_reset() is so
expensive that an extra gettimeofday() wouldn't change much.Agreed
Otherwise
initializing to NULL should be enough.Julien, I would prefer an extra GetCurrentTimestamp(). So, I've opted
to use the common unconditionalstats_reset = GetCurrentTimestamp();
for an entire entry_reset function due to the following:
1. It will be uniform for stats_reset and minmax_stats_reset
2. As you mentioned, it wouldn't change a much
3. The most common way to use this function is to reset all statements
meaning that GetCurrentTimestamp() will be called anyway to update the
value of stats_reset field in pg_stat_statements_info view
4. Actually I would like that pg_stat_statements_reset function was
able to return the value of stats_reset as its result. This could give
to the sampling solutions the ability to check if the last reset (of
any type) was performed by this solution or any other reset was
performed by someone else. It seems valuable to me, but it changes the
result type of the pg_stat_statements_reset() function, so I don't know
if we can do that right now.
I'm fine with always getting the current timestamp when calling the function.
I'm not sure about returning the ts. If you need it you could call SELECT
now() FROM pg_stat_statements_reset() (or clock_timestamp()). It won't be
entirely accurate but since the function will have an exclusive lock during the
whole execution that shouldn't be a problem. Now you're already adding a new
version of the C function so I guess that it wouldn't require any additional
effort so why not.
On Fri, Apr 01, 2022 at 01:01:53PM -0700, Andres Freund wrote:
Hi,
On 2022-04-01 22:47:02 +0300, Andrei Zubkov wrote:
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL); + + if (entry) { + /* Found */ + if (minmax_only) { + /* When requested reset only min/max statistics of an entry */ + entry_counters = &entry->counters; + for (int kind = 0; kind < PGSS_NUMKIND; kind++) + { + entry_counters->max_time[kind] = 0; + entry_counters->min_time[kind] = 0; + } + entry->minmax_stats_since = stats_reset; + } + else + { + /* Remove the key otherwise */ + hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL); + num_remove++; + } + }It seems decidedly not great to have four copies of this code. It was already
not great before, but this patch makes the duplicated section go from four
lines to 20 or so.
+1
Hi,
On Fri, 2022-04-01 at 13:01 -0700, Andres Freund wrote:
It seems decidedly not great to have four copies of this code. It was
already
not great before, but this patch makes the duplicated section go from
four
lines to 20 or so.
Agreed. I've created the single_entry_reset() function wrapping this
code. I wonder if it should be declared as inline to speedup a little.
On Sat, 2022-04-02 at 15:10 +0800, Julien Rouhaud wrote:
However
we can test at least functionality of stats_reset field like this:SELECT now() AS ref_ts \gset
SELECT dealloc, stats_reset >= :'ref_ts' FROM
pg_stat_statements_info;
SELECT pg_stat_statements_reset();
SELECT dealloc, stats_reset >= :'ref_ts' FROM
pg_stat_statements_info;Does it seems reasonable?
It looks reasonable, especially if the patch adds a new mode for the
reset
function.
I've implemented this test.
Checking
of only execution stats seems enough to me - in most cases we can't
check planning stats with such test anyway.
What do you think about it?Ah I see. I guess we could set plan_cache_mode to force_generic_plan
to make
sure we go though planning. But otherwise just adding a comment
saying that
the test has to be compatible with different plan caching approach
would be
fine with me.
Set plan_cache_mode seems a little bit excess to me. And maybe in the
future some another plan caching strategies will be implementd with
coresponding settings.. So I've just left a comment there.
On Sat, 2022-04-02 at 15:21 +0800, Julien Rouhaud wrote:
I'm not sure about returning the ts. If you need it you could call
SELECT
now() FROM pg_stat_statements_reset() (or clock_timestamp()). It
won't be
entirely accurate but since the function will have an exclusive lock
during the
whole execution that shouldn't be a problem. Now you're already
adding a new
version of the C function so I guess that it wouldn't require any
additional
effort so why not.
I think that if we can do it in accurate way and there is no obvious
side effects, why not to try it...
Changing of pg_stat_statements_reset function result caused a
confiderable tests update. Also, I'm not sure that my description of
this feature in the docs is blameless..
After all, I'm a little bit in doubt about this feature, so I'm ready
to rollback it.
v9 attached
--
regards, Andrei
Attachments:
v9-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v9-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 78f3e2e7bda2683b3aeb30b28fbbe60ed781db1d Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Sat, 2 Apr 2022 12:30:11 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement. pg_stat_statements_reset() function is now returns
this timestamp as a result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 +++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++
.../pg_stat_statements/pg_stat_statements.c | 164 ++++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 58 ++-
9 files changed, 756 insertions(+), 158 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..15d8b07bb97 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -109,7 +109,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -121,10 +121,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -206,7 +206,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -215,10 +215,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -241,7 +241,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -255,10 +255,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -282,10 +282,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -335,7 +335,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -343,10 +343,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -394,7 +394,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -403,10 +403,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -482,7 +482,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -490,10 +490,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -524,7 +524,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -533,10 +533,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -589,17 +589,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -642,7 +642,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -651,10 +651,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -667,8 +667,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -682,10 +682,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -701,9 +701,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -712,12 +712,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,11 +731,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -744,11 +744,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -761,12 +761,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -775,16 +775,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -799,10 +799,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -857,7 +857,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -874,16 +874,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1077,4 +1086,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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;
+ query | reset_ts_match
+---------------------------+----------------
+ SELECT $1,$2 AS "STMTTS2" | f
+ SELECT $1 AS "STMTTS1" | t
+(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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..9adc1a4d872
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..9428163e990 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,8 @@ 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 int single_entry_reset(pgssEntry *entry, TimestampTz stats_reset, 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,
@@ -649,6 +655,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 */
@@ -1350,11 +1358,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;
@@ -1399,18 +1419,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_10(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();
}
@@ -1422,7 +1458,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1471,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1594,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1676,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));
@@ -1693,6 +1746,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);
}
@@ -1764,6 +1819,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1831,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1945,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;
@@ -2431,10 +2494,41 @@ gc_fail:
}
/*
- * Release entries corresponding to parameters passed.
+ * Reset a single entry in requested mode.
+ * Return 1 when the entry was removed and 0 otherwise
*/
-static void
-entry_reset(Oid userid, Oid dbid, uint64 queryid)
+static int
+single_entry_reset(pgssEntry *entry, TimestampTz stats_reset, bool minmax_only)
+{
+ Counters *entry_counters;
+
+ if (entry) {
+ if (minmax_only) {
+ /* When requested reset only min/max statistics of an entry */
+ entry_counters = &entry->counters;
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++)
+ {
+ entry_counters->max_time[kind] = 0;
+ entry_counters->min_time[kind] = 0;
+ }
+ entry->minmax_stats_since = stats_reset;
+ }
+ else
+ {
+ /* Remove the key otherwise */
+ hash_search(pgss_hash, &entry->key, HASH_REMOVE, NULL);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Reset entries corresponding to parameters passed.
+ */
+static TimestampTz
+entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
@@ -2442,6 +2536,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,
@@ -2451,6 +2546,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. */
@@ -2459,23 +2556,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ num_remove += single_entry_reset(entry, stats_reset, minmax_only);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ num_remove += single_entry_reset(entry, stats_reset, minmax_only);
}
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)
{
@@ -2483,8 +2578,7 @@ 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++;
+ num_remove += single_entry_reset(entry, stats_reset, minmax_only);
}
}
}
@@ -2494,8 +2588,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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++;
+ num_remove += single_entry_reset(entry, stats_reset, minmax_only);
}
}
@@ -2509,7 +2602,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;
@@ -2547,6 +2639,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..63b603e65ff 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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;
+
+-- 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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..9fac0270347 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -142,7 +142,10 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -153,7 +156,10 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -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 contain zero until this statement
+ is executed fist 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 contain zero until this statement
+ is executed fist 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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>
@@ -589,6 +623,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 execution and planning 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>
--
2.31.1
On Sat, Apr 02, 2022 at 01:12:54PM +0300, Andrei Zubkov wrote:
On Fri, 2022-04-01 at 13:01 -0700, Andres Freund wrote:
It seems decidedly not great to have four copies of this code. It was
already
not great before, but this patch makes the duplicated section go from
four
lines to 20 or so.Agreed. I've created the single_entry_reset() function wrapping this
code. I wonder if it should be declared as inline to speedup a little.
Maybe a macro would be better here? I don't know if that's generally ok or
just old and not-that-great code, but there are other places relying on macros
when a plain function call isn't that convenient (like here returning 0 or 1 as
a hack for incrementing num_remove), for instance in hba.c.
On Sat, 2022-04-02 at 15:21 +0800, Julien Rouhaud wrote:
I'm not sure about returning the ts. If you need it you could call
SELECT
now() FROM pg_stat_statements_reset() (or clock_timestamp()). It
won't be
entirely accurate but since the function will have an exclusive lock
during the
whole execution that shouldn't be a problem. Now you're already
adding a new
version of the C function so I guess that it wouldn't require any
additional
effort so why not.I think that if we can do it in accurate way and there is no obvious
side effects, why not to try it...
Changing of pg_stat_statements_reset function result caused a
confiderable tests update. Also, I'm not sure that my description of
this feature in the docs is blameless..After all, I'm a little bit in doubt about this feature, so I'm ready
to rollback it.
Well, I personally don't think that I would need it for powa as it's designed
to have very frequent snapshot. I know you have a different approach in
pg_profile, but I'm not sure it will be that useful for you either?
On Sat, 2022-04-02 at 18:56 +0800, Julien Rouhaud wrote:
Maybe a macro would be better here? I don't know if that's generally
ok or
just old and not-that-great code, but there are other places relying
on macros
when a plain function call isn't that convenient (like here returning
0 or 1 as
a hack for incrementing num_remove), for instance in hba.c.
Yes, it is not very convenient and not looks pretty, so I'll try a
macro here soon.
I think that if we can do it in accurate way and there is no
obvious
side effects, why not to try it...
Changing of pg_stat_statements_reset function result caused a
confiderable tests update. Also, I'm not sure that my description
of
this feature in the docs is blameless..After all, I'm a little bit in doubt about this feature, so I'm
ready
to rollback it.Well, I personally don't think that I would need it for powa as it's
designed
to have very frequent snapshot. I know you have a different approach
in
pg_profile, but I'm not sure it will be that useful for you either?
Of course I can do some workaround if the accurate reset time will be
unavailable. I just want to do the whole thing if it doesn't hurt. If
we have a plenty of timestamps saved now, I think it is a good idea to
have then bound to some milestones. At least it is a pretty equal join
condition between samples.
But if you think we should avoid returning ts here I won't insist on
that.
On Sat, 2022-04-02 at 14:11 +0300, Andrei Zubkov wrote:
On Sat, 2022-04-02 at 18:56 +0800, Julien Rouhaud wrote:
Maybe a macro would be better here? I don't know if that's
generally
ok or
just old and not-that-great code, but there are other places
relying
on macros
when a plain function call isn't that convenient (like here
returning
0 or 1 as
a hack for incrementing num_remove), for instance in hba.c.Yes, it is not very convenient and not looks pretty, so I'll try a
macro here soon.
Implemented SINGLE_ENTRY_RESET as a macro.
v10 attached
--
regards, Andrei
Attachments:
v10-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v10-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 83ef072fd7406509d4eb0e54d36900f5aeb16f1e Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Sat, 2 Apr 2022 14:58:04 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement. pg_stat_statements_reset() function is now returns
this timestamp as a result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 +++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++
.../pg_stat_statements/pg_stat_statements.c | 153 ++++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 58 ++-
9 files changed, 745 insertions(+), 158 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..15d8b07bb97 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -109,7 +109,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -121,10 +121,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -206,7 +206,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -215,10 +215,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -241,7 +241,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -255,10 +255,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -282,10 +282,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -335,7 +335,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -343,10 +343,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -394,7 +394,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -403,10 +403,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -482,7 +482,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -490,10 +490,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -524,7 +524,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -533,10 +533,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -589,17 +589,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -642,7 +642,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -651,10 +651,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -667,8 +667,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -682,10 +682,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -701,9 +701,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -712,12 +712,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,11 +731,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -744,11 +744,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -761,12 +761,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -775,16 +775,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -799,10 +799,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -857,7 +857,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -874,16 +874,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1077,4 +1086,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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;
+ query | reset_ts_match
+---------------------------+----------------
+ SELECT $1,$2 AS "STMTTS2" | f
+ SELECT $1 AS "STMTTS1" | t
+(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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..9adc1a4d872
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..7d55758724e 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,18 +1418,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_10(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();
}
@@ -1422,7 +1457,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1470,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1593,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1675,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));
@@ -1693,6 +1745,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);
}
@@ -1764,6 +1818,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1830,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1944,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;
@@ -2430,18 +2492,40 @@ gc_fail:
record_gc_qtexts();
}
+#define SINGLE_ENTRY_RESET \
+if (entry) { \
+ if (minmax_only) { \
+ /* When requested reset only min/max statistics of an entry */ \
+ entry_counters = &entry->counters; \
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
+ { \
+ entry_counters->max_time[kind] = 0; \
+ entry_counters->min_time[kind] = 0; \
+ } \
+ entry->minmax_stats_since = stats_reset; \
+ } \
+ else \
+ { \
+ /* Remove the key otherwise */ \
+ hash_search(pgss_hash, &entry->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;
+ Counters *entry_counters;
FILE *qfile;
long num_entries;
long num_remove = 0;
pgssHashKey key;
+ TimestampTz stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
@@ -2451,6 +2535,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. */
@@ -2459,23 +2545,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET;
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ SINGLE_ENTRY_RESET;
}
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)
{
@@ -2483,8 +2567,7 @@ 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;
}
}
}
@@ -2494,8 +2577,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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;
}
}
@@ -2509,7 +2591,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;
@@ -2547,6 +2628,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..63b603e65ff 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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;
+
+-- 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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..9fac0270347 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -142,7 +142,10 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -153,7 +156,10 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -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 contain zero until this statement
+ is executed fist 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 contain zero until this statement
+ is executed fist 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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>
@@ -589,6 +623,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 execution and planning 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>
--
2.31.1
The tests for this seem to need adjustments.
[12:41:09.403] test pg_stat_statements ... FAILED 180 ms
diff -U3 /tmp/cirrus-ci-build/contrib/pg_stat_statements/expected/pg_stat_statements.out
/tmp/cirrus-ci-build/contrib/pg_stat_statements/results/pg_stat_statements.out
--- /tmp/cirrus-ci-build/contrib/pg_stat_statements/expected/pg_stat_statements.out
2022-04-02 12:37:42.201823383 +0000
+++ /tmp/cirrus-ci-build/contrib/pg_stat_statements/results/pg_stat_statements.out
2022-04-02 12:41:09.219563204 +0000
@@ -1174,8 +1174,8 @@
ORDER BY query;
query | reset_ts_match
---------------------------+----------------
- SELECT $1,$2 AS "STMTTS2" | f
SELECT $1 AS "STMTTS1" | t
+ SELECT $1,$2 AS "STMTTS2" | f
(2 rows)
-- check that minmax reset does not set stats_reset
Hm. Is this a collation problem?
Greg Stark <stark@mit.edu> writes:
The tests for this seem to need adjustments.
[12:41:09.403] test pg_stat_statements ... FAILED 180 ms
diff -U3 /tmp/cirrus-ci-build/contrib/pg_stat_statements/expected/pg_stat_statements.out /tmp/cirrus-ci-build/contrib/pg_stat_statements/results/pg_stat_statements.out --- /tmp/cirrus-ci-build/contrib/pg_stat_statements/expected/pg_stat_statements.out 2022-04-02 12:37:42.201823383 +0000 +++ /tmp/cirrus-ci-build/contrib/pg_stat_statements/results/pg_stat_statements.out 2022-04-02 12:41:09.219563204 +0000 @@ -1174,8 +1174,8 @@ ORDER BY query; query | reset_ts_match ---------------------------+---------------- - SELECT $1,$2 AS "STMTTS2" | f SELECT $1 AS "STMTTS1" | t + SELECT $1,$2 AS "STMTTS2" | f (2 rows)
-- check that minmax reset does not set stats_reset
Hm. Is this a collation problem?
Yeah, looks like it. ORDER BY query COLLATE "C" might work better.
regards, tom lane
Hi Greg,
On Sat, 2022-04-02 at 17:38 -0400, Greg Stark wrote:
The tests for this seem to need adjustments.
[12:41:09.403] test pg_stat_statements ... FAILED 180 ms
query | reset_ts_match
---------------------------+----------------
- SELECT $1,$2 AS "STMTTS2" | f
SELECT $1 AS "STMTTS1" | t
+ SELECT $1,$2 AS "STMTTS2" | f
(2 rows)-- check that minmax reset does not set stats_reset
Hm. Is this a collation problem?
Of course, thank you! I've forgot to set collation here.
v11 attached
--
regards, Andrei
Attachments:
v11-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v11-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From c5900f1c689b2a74edbc30b66c9a73e25b85484a Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Sun, 3 Apr 2022 07:28:59 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement. pg_stat_statements_reset() function is now returns
this timestamp as a result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 +++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++
.../pg_stat_statements/pg_stat_statements.c | 153 ++++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 58 ++-
9 files changed, 745 insertions(+), 158 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..f5ff4031c59 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -109,7 +109,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -121,10 +121,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -206,7 +206,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -215,10 +215,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -241,7 +241,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -255,10 +255,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -282,10 +282,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -335,7 +335,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -343,10 +343,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -394,7 +394,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -403,10 +403,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -482,7 +482,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -490,10 +490,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -524,7 +524,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -533,10 +533,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -589,17 +589,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -642,7 +642,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -651,10 +651,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -667,8 +667,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -682,10 +682,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -701,9 +701,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -712,12 +712,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,11 +731,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -744,11 +744,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -761,12 +761,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -775,16 +775,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -799,10 +799,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -857,7 +857,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -874,16 +874,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1077,4 +1086,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..9adc1a4d872
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..7d55758724e 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,18 +1418,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_10(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();
}
@@ -1422,7 +1457,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1470,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1593,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1675,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));
@@ -1693,6 +1745,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);
}
@@ -1764,6 +1818,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1830,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1944,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;
@@ -2430,18 +2492,40 @@ gc_fail:
record_gc_qtexts();
}
+#define SINGLE_ENTRY_RESET \
+if (entry) { \
+ if (minmax_only) { \
+ /* When requested reset only min/max statistics of an entry */ \
+ entry_counters = &entry->counters; \
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
+ { \
+ entry_counters->max_time[kind] = 0; \
+ entry_counters->min_time[kind] = 0; \
+ } \
+ entry->minmax_stats_since = stats_reset; \
+ } \
+ else \
+ { \
+ /* Remove the key otherwise */ \
+ hash_search(pgss_hash, &entry->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;
+ Counters *entry_counters;
FILE *qfile;
long num_entries;
long num_remove = 0;
pgssHashKey key;
+ TimestampTz stats_reset;
if (!pgss || !pgss_hash)
ereport(ERROR,
@@ -2451,6 +2535,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. */
@@ -2459,23 +2545,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET;
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ SINGLE_ENTRY_RESET;
}
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)
{
@@ -2483,8 +2567,7 @@ 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;
}
}
}
@@ -2494,8 +2577,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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;
}
}
@@ -2509,7 +2591,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;
@@ -2547,6 +2628,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..9d294a053ac 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..9fac0270347 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -142,7 +142,10 @@
<para>
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -153,7 +156,10 @@
<para>
Maximum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
@@ -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 contain zero until this statement
+ is executed fist 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 contain zero until this statement
+ is executed fist 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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>
@@ -589,6 +623,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 execution and planning 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>
--
2.31.1
On Sun, Apr 03, 2022 at 07:32:47AM +0300, Andrei Zubkov wrote:
v11 attached
+ /* When requested reset only min/max statistics of an entry */ \
+ entry_counters = &entry->counters; \
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
+ { \
+ entry_counters->max_time[kind] = 0; \
+ entry_counters->min_time[kind] = 0; \
+ } \
[...]
+static TimestampTz
+entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only)
{
HASH_SEQ_STATUS hash_seq;
pgssEntry *entry;
+ Counters *entry_counters;
Do we really need an extra variable? Why not simply using
entry->counters.xxx_time[kind]?
Also, I think it's better to make the macro more like function looking, so
SINGLE_ENTRY_RESET().
index f2e822acd3..c2af29866b 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
I don't think this bring any useful coverage.
Minimum time spent planning the statement, in milliseconds
(if <varname>pg_stat_statements.track_planning</varname> is enabled,
- otherwise zero)
+ otherwise zero), this field will contain zero until this statement
+ is planned fist 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>
I think this need some rewording (and s/fist/first). Maybe:
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 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.
<primary>pg_stat_statements_reset</primary>
</indexterm>
@@ -589,6 +623,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 execution and planning time will be reset (i.e.
Nitpicking: I would say planning and execution time, as the fields are in this
order in the view/function.
Hi Julien,
On Sun, 2022-04-03 at 15:07 +0800, Julien Rouhaud wrote:
+static TimestampTz +entry_reset(Oid userid, Oid dbid, uint64 queryid, bool minmax_only) { HASH_SEQ_STATUS hash_seq; pgssEntry *entry; + Counters *entry_counters;Do we really need an extra variable? Why not simply using
entry->counters.xxx_time[kind]?Also, I think it's better to make the macro more like function
looking, so
SINGLE_ENTRY_RESET().
Agreed with the both, I'll fix it.
index f2e822acd3..c2af29866b 100644 --- a/contrib/pg_stat_statements/sql/oldextversions.sql +++ b/contrib/pg_stat_statements/sql/oldextversions.sql @@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8'; \d pg_stat_statements SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9'; +\d pg_stat_statements +\d pg_stat_statements_info +SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);I don't think this bring any useful coverage.
I feel the same, but I've done it like previous tests (versions 1.7 and
1.8). Am I miss something here? Do you think we should remove these
tests completly?
I think this need some rewording (and s/fist/first). Maybe:
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 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.
Thanks a lot!
<primary>pg_stat_statements_reset</primary> </indexterm> @@ -589,6 +623,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 execution and planning time will be reset (i.e.Nitpicking: I would say planning and execution time, as the fields
are in this
order in the view/function.
Agreed.
--
regards, Andrei
I've attached v12 of a patch. The only unsolved issue now is the
following:
On Sun, 2022-04-03 at 15:07 +0800, Julien Rouhaud wrote:
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9'; +\d pg_stat_statements +\d pg_stat_statements_info +SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);I don't think this bring any useful coverage.
It is a little bit unclear to me what is the best solution here.
--
regards, Andrei
Attachments:
v12-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v12-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 9359b7dfdadeb7a672c146030995626150acf231 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Sun, 3 Apr 2022 12:21:47 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
This patch adds stats_since column to the pg_stat_statements view. This 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 interval for each statement. Besides it can be used
by sampling solutions to detect situations when a statement was evicted and
returned back between samples.
Such sampling solution could derive any pg_stat_statements statistic value for
an interval between two samples with except of all min/max statistics. To
address this issue this patch adds the ability to reset min/max
statistics independently of statement reset using the new minmax_only parameter
of the pg_stat_statements_reset(userid oid, dbid oid, queryid bigint,
minmax_only boolean) function.
Timestamp of such reset is stored in the minmax_stats_since field for
each statement. pg_stat_statements_reset() function is now returns
this timestamp as a result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 +++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++
.../pg_stat_statements/pg_stat_statements.c | 151 ++++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 66 +++-
9 files changed, 747 insertions(+), 162 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..f5ff4031c59 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -109,7 +109,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -121,10 +121,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -206,7 +206,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -215,10 +215,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -241,7 +241,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -255,10 +255,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -282,10 +282,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -335,7 +335,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -343,10 +343,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -394,7 +394,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -403,10 +403,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -482,7 +482,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -490,10 +490,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -524,7 +524,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -533,10 +533,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -589,17 +589,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -642,7 +642,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -651,10 +651,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -667,8 +667,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -682,10 +682,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -701,9 +701,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -712,12 +712,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,11 +731,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -744,11 +744,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -761,12 +761,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -775,16 +775,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -799,10 +799,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -857,7 +857,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -874,16 +874,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1077,4 +1086,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..9adc1a4d872
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..441841f2851 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,18 +1418,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_10(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();
}
@@ -1422,7 +1457,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1470,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1593,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1675,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));
@@ -1693,6 +1745,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);
}
@@ -1764,6 +1818,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1830,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1944,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;
@@ -2430,11 +2492,30 @@ gc_fail:
record_gc_qtexts();
}
+#define SINGLE_ENTRY_RESET() \
+if (entry) { \
+ if (minmax_only) { \
+ /* When requested reset only min/max statistics of an entry */ \
+ for (int kind = 0; kind < PGSS_NUMKIND; kind++) \
+ { \
+ entry->counters.max_time[kind] = 0; \
+ entry->counters.min_time[kind] = 0; \
+ } \
+ entry->minmax_stats_since = stats_reset; \
+ } \
+ else \
+ { \
+ /* Remove the key otherwise */ \
+ hash_search(pgss_hash, &entry->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;
@@ -2442,6 +2523,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,
@@ -2451,6 +2533,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. */
@@ -2459,23 +2543,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET();
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ SINGLE_ENTRY_RESET();
}
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)
{
@@ -2483,8 +2565,7 @@ 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();
}
}
}
@@ -2494,8 +2575,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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();
}
}
@@ -2509,7 +2589,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;
@@ -2547,6 +2626,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..9d294a053ac 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..f4b4697dc93 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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 contain 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 contain 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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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>
@@ -589,6 +623,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>
--
2.31.1
Hi,
On Sun, Apr 03, 2022 at 12:29:43PM +0300, Andrei Zubkov wrote:
I've attached v12 of a patch. The only unsolved issue now is the
following:On Sun, 2022-04-03 at 15:07 +0800, Julien Rouhaud wrote:
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9'; +\d pg_stat_statements +\d pg_stat_statements_info +SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);I don't think this bring any useful coverage.
It is a little bit unclear to me what is the best solution here.
Sorry, I missed that there were some similar tests already for previous
versions. This was probably discussed and agreed before, so +1 to be
consistent with the new versions.
The patch looks good to me, although I will do a full review to make sure I
didn't miss anything.
Just another minor nitpicking after a quick look:
+ This field will be zero if ...
[...]
+ this field will contain zero until this statement ...
The wording should be consistent, so either "will be zero" or "will contain
zero" everywhere. I'm personally fine with any, but maybe a native English
will think one is better.
Julien,
On Sun, 2022-04-03 at 17:56 +0800, Julien Rouhaud wrote:
Just another minor nitpicking after a quick look:
+ This field will be zero if ... [...] + this field will contain zero until this statement ...The wording should be consistent, so either "will be zero" or "will
contain
zero" everywhere. I'm personally fine with any, but maybe a native
English
will think one is better.
Agreed.
Searching the docs I've fond out that "will contain" usually used with
the description of contained structure rather then a simple value. So
I'll use a "will be zero" in the next version after your review.
--
regards, Andrei
Hi,
On Sun, Apr 03, 2022 at 01:24:40PM +0300, Andrei Zubkov wrote:
On Sun, 2022-04-03 at 17:56 +0800, Julien Rouhaud wrote:
Just another minor nitpicking after a quick look:
+ This field will be zero if ... [...] + this field will contain zero until this statement ...The wording should be consistent, so either "will be zero" or "will
contain
zero" everywhere.� I'm personally fine with any, but maybe a native
English
will think one is better.Agreed.
Searching the docs I've fond out that "will contain" usually used with
the description of contained structure rather then a simple value. So
I'll use a "will be zero" in the next version after your review.
Ok!
So last round of review.
- the commit message:
It should probably mention the mimnax_stats_since at the beginning. Also, both
the view and the function contain those new field.
Minor rephrasing:
s/evicted and returned back/evicted and stored again/?
s/with except of all/with the exception of all/
s/is now returns/now returns/
- code:
+#define SINGLE_ENTRY_RESET() \
+if (entry) { \
[...]
It's not great to rely on caller context too much. I think it would be better
to pass at least the entry as a parameter (maybe e?) to the macro for more
clarity. I'm fine with keeping minmax_only, stats_reset and num_remove as is.
Apart from that I think this is ready!
Hi Julien,
Thank you very much for your work on this patch!
On Mon, 2022-04-04 at 10:31 +0800, Julien Rouhaud wrote:
- the commit message:
It should probably mention the mimnax_stats_since at the beginning.
Also, both
the view and the function contain those new field.Minor rephrasing:
s/evicted and returned back/evicted and stored again/?
s/with except of all/with the exception of all/
s/is now returns/now returns/
Agreed, commit message updated.
- code:
+#define SINGLE_ENTRY_RESET() \ +if (entry) { \ [...]It's not great to rely on caller context too much.
Yes, I was thinking about it. But using 4 parameters seemed strange to
me.
I think it would be better
to pass at least the entry as a parameter (maybe e?) to the macro for
more
clarity. I'm fine with keeping minmax_only, stats_reset and
num_remove as is.
Using an entry as a macro parameter looks good, I'm fine with "e".
Apart from that I think this is ready!
v13 attached
--
regards, Andrei
Attachments:
v13-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v13-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From b6b1eab21f5b873ec9a1bb13b82cca7d6bcaab32 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Mon, 4 Apr 2022 09:46:31 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 3 +-
.../expected/oldextversions.out | 61 +++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 108 ++++++
.../pg_stat_statements/pg_stat_statements.c | 151 ++++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 8 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 66 +++-
9 files changed, 747 insertions(+), 162 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 7fabd96f38d..edc40c8bbfb 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,8 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.8--1.9.sql \
+DATA = pg_stat_statements--1.4.sql \
+ pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index f18c08838f5..70877948491 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -136,4 +136,65 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
(1 row)
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\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 | | |
+ blk_read_time | double precision | | |
+ blk_write_time | double precision | | |
+ wal_records | bigint | | |
+ wal_fpi | bigint | | |
+ wal_bytes | numeric | | |
+
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index e0abe34bb6a..f5ff4031c59 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -109,7 +109,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -121,10 +121,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -206,7 +206,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -215,10 +215,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -241,7 +241,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -255,10 +255,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -282,10 +282,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -335,7 +335,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -343,10 +343,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -394,7 +394,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -403,10 +403,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -482,7 +482,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -490,10 +490,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -524,7 +524,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -533,10 +533,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -589,17 +589,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -642,7 +642,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -651,10 +651,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -667,8 +667,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -682,10 +682,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -701,9 +701,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -712,12 +712,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,11 +731,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -744,11 +744,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -761,12 +761,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -775,16 +775,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -799,10 +799,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -857,7 +857,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -874,16 +874,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1077,4 +1086,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
new file mode 100644
index 00000000000..9adc1a4d872
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -0,0 +1,108 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.10'" to load this file. \quit
+
+/* We need to redefine a view and a function */
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ 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_10'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT
+ userid,
+ dbid,
+ toplevel,
+ queryid,
+ query,
+ plans,
+ total_plan_time,
+ min_plan_time,
+ max_plan_time,
+ mean_plan_time,
+ stddev_plan_time,
+ calls,
+ total_exec_time,
+ min_exec_time,
+ max_exec_time,
+ mean_exec_time,
+ stddev_exec_time,
+ rows,
+ shared_blks_hit,
+ shared_blks_read,
+ shared_blks_dirtied,
+ shared_blks_written,
+ local_blks_hit,
+ local_blks_read,
+ local_blks_dirtied,
+ local_blks_written,
+ temp_blks_read,
+ temp_blks_written,
+ blk_read_time,
+ blk_write_time,
+ wal_records,
+ wal_fpi,
+ wal_bytes,
+ stats_since,
+ minmax_stats_since
+ FROM pg_stat_statements(true);
+
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 55786ae84f2..d3acab18ad6 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -88,7 +88,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20201227;
+static const uint32 PGSS_FILE_HEADER = 0x20210322;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -121,7 +121,8 @@ typedef enum pgssVersion
PGSS_V1_2,
PGSS_V1_3,
PGSS_V1_8,
- PGSS_V1_9
+ PGSS_V1_9,
+ PGSS_V1_10
} pgssVersion;
typedef enum pgssStoreKind
@@ -165,9 +166,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
@@ -209,12 +210,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -298,10 +301,12 @@ void _PG_fini(void);
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_10);
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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -345,7 +350,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,
@@ -649,6 +654,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 */
@@ -1350,11 +1357,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;
@@ -1399,18 +1418,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_10(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();
}
@@ -1422,7 +1457,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#define PG_STAT_STATEMENTS_COLS_V1_8 32
#define PG_STAT_STATEMENTS_COLS_V1_9 33
-#define PG_STAT_STATEMENTS_COLS 33 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 35
+#define PG_STAT_STATEMENTS_COLS 35 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1434,6 +1470,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_10(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_10, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_9(PG_FUNCTION_ARGS)
{
@@ -1547,6 +1593,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_9)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_10:
+ if (api_version != PGSS_V1_10)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1625,6 +1675,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));
@@ -1693,6 +1745,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);
}
@@ -1764,6 +1818,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
Int32GetDatum(-1));
values[i++] = wal_bytes;
}
+ if (api_version >= PGSS_V1_10)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1771,6 +1830,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_3 ? PG_STAT_STATEMENTS_COLS_V1_3 :
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
+ api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1884,6 +1944,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;
@@ -2430,11 +2492,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;
@@ -2442,6 +2523,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,
@@ -2451,6 +2533,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. */
@@ -2459,23 +2543,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2483,8 +2565,7 @@ 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);
}
}
}
@@ -2494,8 +2575,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2509,7 +2589,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;
@@ -2547,6 +2626,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 2f1ce6ed507..0747e481383 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.9'
+default_version = '1.10'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index f2e822acd3e..c2af29866ba 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -36,4 +36,12 @@ AlTER EXTENSION pg_stat_statements UPDATE TO '1.8';
\d pg_stat_statements
SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+\d pg_stat_statements
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..9d294a053ac 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 3a7e36bd13c..ca715caffe5 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -379,6 +393,25 @@
Total amount of WAL generated by the statement in bytes
</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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -570,7 +603,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>
@@ -589,6 +623,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>
--
2.31.1
Hi,
On Mon, Apr 04, 2022 at 09:59:04AM +0300, Andrei Zubkov wrote:
Minor rephrasing:
s/evicted and returned back/evicted and stored again/?
s/with except of all/with the exception of all/
s/is now returns/now returns/Agreed, commit message updated.
- code:
+#define SINGLE_ENTRY_RESET() \ +if (entry) { \ [...]It's not great to rely on caller context too much.
Yes, I was thinking about it. But using 4 parameters seemed strange to
me.� I think it would be better
to pass at least the entry as a parameter (maybe e?) to the macro for
more
clarity.� I'm fine with keeping minmax_only, stats_reset and
num_remove as is.Using an entry as a macro parameter looks good, I'm fine with "e".
Apart from that I think this is ready!
v13 attached
Thanks a lot! I'm happy with this version, so I'm marking it as Ready for
Committer.
Hi,
I've rebased this patch so that it can be applied after 57d6aea00fc.
v14 attached
--
regards, Andrei
Attachments:
v14-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v14-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 6c541f3001d952e72e5d865fde09de3fb4f36d10 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 8 Apr 2022 23:12:55 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../expected/oldextversions.out | 118 +++---
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.9--1.10.sql | 16 +-
.../pg_stat_statements/pg_stat_statements.c | 128 +++++--
.../pg_stat_statements/sql/oldextversions.sql | 7 +-
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 66 +++-
7 files changed, 637 insertions(+), 208 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..0634d73bc03 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -138,7 +138,7 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
-- New function pg_stat_statement_info, and new function
-- and view for pg_stat_statements introduced in 1.9
-AlTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
SELECT pg_get_functiondef('pg_stat_statements_info'::regproc);
pg_get_functiondef
-------------------------------------------------------------------------------------------------------------------------
@@ -194,55 +194,79 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+\d pg_stat_statements_info
+ View "public.pg_stat_statements_info"
+ Column | Type | Collation | Nullable | Default
+-------------+--------------------------+-----------+----------+---------
+ dealloc | bigint | | |
+ stats_reset | timestamp with time zone | | |
+
+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)+
+ RETURNS void +
+ LANGUAGE c +
+ PARALLEL SAFE STRICT +
+ AS '$libdir/pg_stat_statements', $function$pg_stat_statements_reset_1_7$function$ +
+
+(1 row)
+
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+ERROR: permission denied for function pg_stat_statements_reset
+RESET SESSION AUTHORIZATION;
-- New functions and views for pg_stat_statements in 1.10
AlTER EXTENSION pg_stat_statements UPDATE TO '1.10';
\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 | | |
- blk_read_time | double precision | | |
- 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 | | |
+ 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 8f7f93172a2..177e3eb7836 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -134,7 +134,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -146,10 +146,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -231,7 +231,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -240,10 +240,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -266,7 +266,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | f
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -280,10 +280,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -307,10 +307,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -360,7 +360,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -368,10 +368,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -419,7 +419,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -428,10 +428,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -507,7 +507,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -515,10 +515,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -549,7 +549,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -558,10 +558,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -614,17 +614,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -667,7 +667,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -676,10 +676,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -692,8 +692,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -707,10 +707,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -726,9 +726,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -737,12 +737,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -756,11 +756,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -769,11 +769,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -786,12 +786,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -800,16 +800,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -824,10 +824,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -882,7 +882,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -899,16 +899,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1102,4 +1111,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
index 811813c4915..7ca8d42f831 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.9--1.10.sql
@@ -6,10 +6,13 @@
/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
/* Then we can drop them */
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,
@@ -55,7 +58,9 @@ CREATE FUNCTION pg_stat_statements(IN showtext boolean,
OUT jit_optimization_count int8,
OUT jit_optimization_time float8,
OUT jit_emission_count int8,
- OUT jit_emission_time float8
+ OUT jit_emission_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_10'
@@ -64,4 +69,13 @@ LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
CREATE VIEW pg_stat_statements AS
SELECT * FROM pg_stat_statements(true);
+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_10'
+LANGUAGE C STRICT PARALLEL SAFE;
+
GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 1ca67ef6234..d5ebade01aa 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -162,9 +162,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
@@ -220,12 +220,14 @@ typedef struct pgssGlobalStats
*/
typedef struct pgssEntry
{
- pgssHashKey key; /* hash key of entry - MUST BE FIRST */
- Counters counters; /* the statistics for this query */
- 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 */
- slock_t mutex; /* protects the counters only */
+ pgssHashKey key; /* hash key of entry - MUST BE FIRST */
+ Counters counters; /* the statistics for this query */
+ 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;
/*
@@ -309,6 +311,7 @@ void _PG_fini(void);
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_10);
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);
@@ -358,7 +361,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,
@@ -662,6 +665,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 */
@@ -1368,11 +1373,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;
@@ -1436,18 +1453,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_10(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();
}
@@ -1459,8 +1492,8 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
#define PG_STAT_STATEMENTS_COLS_V1_3 23
#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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_10 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1677,6 +1710,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));
@@ -1745,6 +1780,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);
}
@@ -1831,6 +1868,8 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Float8GetDatumFast(tmp.jit_optimization_time);
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_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 :
@@ -1953,6 +1992,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;
@@ -2499,11 +2540,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;
@@ -2511,6 +2571,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,
@@ -2520,6 +2581,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. */
@@ -2528,23 +2591,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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);
+
+ SINGLE_ENTRY_RESET(entry);
- /* Also remove entries for top level statements */
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2552,8 +2613,7 @@ 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);
}
}
}
@@ -2563,8 +2623,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2578,7 +2637,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;
@@ -2616,6 +2674,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..662ee59cb66 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -38,10 +38,15 @@ SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
-- New function pg_stat_statement_info, and new function
-- and view for pg_stat_statements introduced in 1.9
-AlTER EXTENSION pg_stat_statements UPDATE TO '1.9';
+ALTER EXTENSION pg_stat_statements UPDATE TO '1.9';
SELECT pg_get_functiondef('pg_stat_statements_info'::regproc);
\d pg_stat_statements
SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
+\d pg_stat_statements_info
+SELECT pg_get_functiondef('pg_stat_statements_reset'::regproc);
+SET SESSION AUTHORIZATION pg_read_all_stats;
+SELECT pg_stat_statements_reset();
+RESET SESSION AUTHORIZATION;
-- New functions and views for pg_stat_statements in 1.10
AlTER EXTENSION pg_stat_statements UPDATE TO '1.10';
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index dffd2c8c187..9d294a053ac 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -105,7 +105,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -129,7 +129,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -140,7 +140,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -174,7 +174,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -207,7 +207,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -236,7 +236,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -255,7 +255,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -278,7 +278,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -299,7 +299,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -310,27 +310,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -345,7 +345,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -366,8 +366,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -442,4 +446,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 45e720e995d..b6481e45b37 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -664,7 +697,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>
@@ -683,6 +717,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>
--
2.31.1
Hi,
I took a quick look at this patch, to see if there's something we
want/can get into v16. The last version was submitted about 9 months
ago, and it doesn't apply cleanly anymore, but the bitrot is fairly
minor. Not sure there's still interest, though.
As for the patch, I wonder if it's unnecessarily complex. It adds *two*
timestamps for each pg_stat_statements entry - one for reset of the
whole entry, one for reset of "min/max" times only.
I can see why the first timestamp (essentially tracking creating of the
entry) is useful. I'd probably call it "created_at" or something like
that, but that's a minor detail. Or maybe stats_reset, which is what we
use in pgstat?
But is the second timestamp for the min/max fields really useful? AFAIK
to perform analysis, people take regular pg_stat_statements snapshots,
which works fine for counters (calculating deltas) but not for gauges
(which need a reset, to track fresh values). But people analyzing this
are already resetting the whole entry, and so the snapshots already are
tracking deltas.
So I'm not convinced actually need the second timestamp.
A couple more comments:
1) I'm not sure why the patch is adding tests of permissions on the
pg_stat_statements_reset function?
2) If we want the second timestamp, shouldn't it also cover resets of
the mean values, not just min/max?
3) I don't understand why the patch is adding "IS NOT NULL AS t" to
various places in the regression tests.
4) I rather dislike the "minmax" naming, because that's often used in
other contexts (for BRIN indexes), and as I mentioned maybe it should
also cover the "mean" fields.
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi Tomas,
On Wed, 2023-01-18 at 17:29 +0100, Tomas Vondra wrote:
I took a quick look at this patch, to see if there's something we
want/can get into v16. The last version was submitted about 9 months
ago, and it doesn't apply cleanly anymore, but the bitrot is fairly
minor. Not sure there's still interest, though.
Thank you for your attention to this patch!
I'm still very interest in this patch. And I think I'll try to rebase
this patch during a week or two if it seems possible to get it in 16..
I'd probably call it "created_at" or something like
that, but that's a minor detail. Or maybe stats_reset, which is what
we
use in pgstat?
Yes there is some naming issue. My thought was the following:
- "stats_reset" is not quite correct here, because the statement entry
moment if definitely not a reset. The field named just as it means -
this is time of the moment from which statistics is collected for this
particular entry.
- "created_at" perfectly matches the purpose of the field, but seems
not such self-explaining to me.
But is the second timestamp for the min/max fields really useful?
AFAIK
to perform analysis, people take regular pg_stat_statements
snapshots,
which works fine for counters (calculating deltas) but not for gauges
(which need a reset, to track fresh values). But people analyzing
this
are already resetting the whole entry, and so the snapshots already
are
tracking deltas.So I'm not convinced actually need the second timestamp.
The main purpose of the patch is to provide means to collecting
solutions to avoid the reset of pgss at all. Just like it happens for
the pg_stat_ views. The only really need of reset is that we can't be
sure that observing statement was not evicted and come back since last
sample. Right now we only can do a whole reset on each sample and see
how many entries will be in pgss hashtable on the next sample - how
close this value to the max. If there is a plenty space in hashtable we
can hope that there was not evictions since last sample. However there
could be reset performed by someone else and we are know nothing about
this.
Having a timestamp in stats_since field we are sure about how long this
statement statistics is tracked. That said sampling solution can
totally avoid pgss resets. Avoiding such resets means avoiding
interference between monitoring solutions.
But if no more resets is done we can't track min/max values, because
they still needs a reset and we can do nothing with such resets - they
are necessary. However I still want to know when min/max reset was
performed. This will help to detect possible interference on such
resets.
A couple more comments:
1) I'm not sure why the patch is adding tests of permissions on the
pg_stat_statements_reset function?2) If we want the second timestamp, shouldn't it also cover resets of
the mean values, not just min/max?
I think that mean values shouldn't be target for a partial reset
because the value for mean values can be easily reconstructed by the
sampling solution without a reset.
3) I don't understand why the patch is adding "IS NOT NULL AS t" to
various places in the regression tests.
The most of tests was copied from the previous version. I'll recheck
them.
4) I rather dislike the "minmax" naming, because that's often used in
other contexts (for BRIN indexes), and as I mentioned maybe it should
also cover the "mean" fields.
Agreed, but I couldn't make it better. Other versions seemed worse to
me...
Regards, Andrei Zubkov
Hi,
I've updated this patch for the current master. Also I have some
additional explanations..
On Wed, 2023-01-18 at 17:29 +0100, Tomas Vondra wrote:
1) I'm not sure why the patch is adding tests of permissions on the
pg_stat_statements_reset function?
I've fixed that
2) If we want the second timestamp, shouldn't it also cover resets of
the mean values, not just min/max?
I think that mean values are not a targets for auxiliary resets because
any sampling solution can easily calculate the mean values between
samples without a reset.
3) I don't understand why the patch is adding "IS NOT NULL AS t" to
various places in the regression tests.
This change is necessary in the current version because the
pg_stat_statements_reset() function will return a timestamp of a reset,
needed for sampling solutions to detect resets, perfermed by someone
else.
Regards
--
Andrei Zubkov
Attachments:
v15-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v15-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 8214db3676e686993bcf73963f78c96baeb04c4e Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 25 Jan 2023 18:13:14 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/oldextversions.out | 70 ++++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
.../pg_stat_statements--1.10--1.11.sql | 81 ++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 7 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 66 +++-
9 files changed, 721 insertions(+), 156 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index edc40c8bbfb..0afb9060fa1 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a2..1e572f1a9e3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -134,7 +134,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -146,10 +146,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -270,7 +270,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -279,10 +279,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -305,7 +305,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | t
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -319,10 +319,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -346,10 +346,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -399,7 +399,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -407,10 +407,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -458,7 +458,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -467,10 +467,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -546,7 +546,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -554,10 +554,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -588,7 +588,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -597,10 +597,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -653,17 +653,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -706,7 +706,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -715,10 +715,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,8 +731,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -746,10 +746,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -765,9 +765,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -776,12 +776,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -795,11 +795,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -808,11 +808,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -825,12 +825,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -839,16 +839,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -863,10 +863,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -921,7 +921,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -938,16 +938,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1141,4 +1150,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..782029727cc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,81 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index ad1fe444969..316bec69e0a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1370,11 +1377,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;
@@ -1438,18 +1457,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();
}
@@ -1462,7 +1497,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1474,6 +1510,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1604,6 +1650,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1681,6 +1731,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));
@@ -1749,6 +1801,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);
}
@@ -1836,6 +1890,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1844,6 +1903,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1952,6 +2012,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;
@@ -2515,11 +2577,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;
@@ -2527,6 +2608,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,
@@ -2536,6 +2618,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. */
@@ -2544,23 +2628,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2568,8 +2650,7 @@ 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);
}
}
}
@@ -2579,8 +2660,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2594,7 +2674,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;
@@ -2632,6 +2711,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225b..683a1f40244 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -127,7 +127,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -151,7 +151,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -162,7 +162,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -196,7 +196,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -229,7 +229,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -258,7 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -277,7 +277,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -300,7 +300,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -321,7 +321,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -332,27 +332,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -367,7 +367,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -388,8 +388,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -464,4 +468,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index efc36da6021..0d1a0ad0108 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -664,7 +697,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>
@@ -683,6 +717,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>
--
2.30.2
Hi,
The final version of this patch should fix meson build and tests.
--
Andrei Zubkov
Attachments:
v16-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v16-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 94784bccd48a83cba58d6017253d0b8f051e159c Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 26 Jan 2023 13:18:11 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/oldextversions.out | 70 ++++
.../expected/pg_stat_statements.out | 361 +++++++++++++-----
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 81 ++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++--
.../pg_stat_statements.control | 2 +-
.../pg_stat_statements/sql/oldextversions.sql | 7 +
.../sql/pg_stat_statements.sql | 149 +++++++-
doc/src/sgml/pgstatstatements.sgml | 66 +++-
10 files changed, 722 insertions(+), 156 deletions(-)
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index edc40c8bbfb..0afb9060fa1 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 9ac5c87c3a2..1e572f1a9e3 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -134,7 +134,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -146,10 +146,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -270,7 +270,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -279,10 +279,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -305,7 +305,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
DROP TABLE pgss_test | 1 | 0 | t | t | t
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -319,10 +319,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -346,10 +346,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DO LANGUAGE plpgsql $$
@@ -399,7 +399,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1::TEXT | 1 | 1
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(5 rows)
@@ -407,10 +407,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -458,7 +458,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2
SELECT PLUS_ONE($1) | 2 | 2
SELECT PLUS_TWO($1) | 2 | 2
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(6 rows)
@@ -467,10 +467,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -546,7 +546,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -554,10 +554,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1;
@@ -588,7 +588,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE IF EXISTS test | 3 | 0
DROP TABLE test | 1 | 0
SELECT $1 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(9 rows)
@@ -597,10 +597,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
@@ -653,17 +653,17 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
FETCH NEXT pgss_cursor | 0 | 1 | 1
REFRESH MATERIALIZED VIEW pgss_matv | 0 | 1 | 13
SELECT generate_series(1, 10) c INTO pgss_select_into | 0 | 1 | 10
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 0 | 0
(13 rows)
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -706,7 +706,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -715,10 +715,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -731,8 +731,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -746,10 +746,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -765,9 +765,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -776,12 +776,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -795,11 +795,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -808,11 +808,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -825,12 +825,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -839,16 +839,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -863,10 +863,10 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE TABLE test ();
@@ -921,7 +921,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements
ALTER TABLE test ADD COLUMN x int | 0 | 1 | 0
CREATE TABLE test () | 0 | 1 | 0
SELECT $1 | 3 | 3 | 3
- SELECT pg_stat_statements_reset() | 0 | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 0 | 1 | 1
SELECT query, plans, calls, rows FROM pg_stat_statements+| 1 | 0 | 0
WHERE query NOT LIKE $1 ORDER BY query COLLATE "C" | | |
(5 rows)
@@ -938,16 +938,25 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
--
@@ -1141,4 +1150,160 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
+(1 row)
+
+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)
+
DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 508b53b4a27..ba7bd83bc99 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..782029727cc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,81 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index ad1fe444969..316bec69e0a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1370,11 +1377,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;
@@ -1438,18 +1457,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();
}
@@ -1462,7 +1497,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1474,6 +1510,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1604,6 +1650,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1681,6 +1731,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));
@@ -1749,6 +1801,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);
}
@@ -1836,6 +1890,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1844,6 +1903,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1952,6 +2012,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;
@@ -2515,11 +2577,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;
@@ -2527,6 +2608,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,
@@ -2536,6 +2618,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. */
@@ -2544,23 +2628,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2568,8 +2650,7 @@ 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);
}
}
}
@@ -2579,8 +2660,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2594,7 +2674,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;
@@ -2632,6 +2711,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 8f5c866225b..683a1f40244 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -127,7 +127,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -151,7 +151,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
@@ -162,7 +162,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = top
--
SET pg_stat_statements.track = 'top';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DO LANGUAGE plpgsql $$
BEGIN
@@ -196,7 +196,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- pg_stat_statements.track = all
--
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -229,7 +229,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -258,7 +258,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- utility commands
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1;
CREATE INDEX test_b ON test(b);
@@ -277,7 +277,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
-- commands of COPY, FETCH, CREATE TABLE AS, CREATE MATERIALIZED VIEW,
-- REFRESH MATERIALIZED VIEW and SELECT INTO
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE pgss_ctas AS SELECT a, 'ctas' b FROM generate_series(1, 10) a;
SELECT generate_series(1, 10) c INTO pgss_select_into;
@@ -300,7 +300,7 @@ SELECT query, plans, calls, rows FROM pg_stat_statements ORDER BY query COLLATE
--
-- Track user activity and reset them
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -321,7 +321,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -332,27 +332,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -367,7 +367,7 @@ DROP TABLE pgss_select_into;
--
-- [re]plan counting
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE TABLE test ();
PREPARE prep1 AS SELECT COUNT(*) FROM test;
EXECUTE prep1;
@@ -388,8 +388,12 @@ SELECT query, plans >= 2 AND plans <= calls AS plans_ok, calls, rows FROM pg_sta
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
--
-- top level handling
@@ -464,4 +468,113 @@ SELECT (
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
+--
+-- statement timestamps
+--
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+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%';
+
DROP EXTENSION pg_stat_statements;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index efc36da6021..0d1a0ad0108 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -664,7 +697,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>
@@ -683,6 +717,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>
--
2.30.2
Hi!
I've attached a new version of a patch - rebase to the current master.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v17-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v17-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 92816cfb26f0ebd35c00a6ce4f25e45f08d83790 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 1 Mar 2023 11:37:53 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../pg_stat_statements/expected/cursors.out | 28 +--
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/level_tracking.out | 72 ++++----
.../expected/oldextversions.out | 70 ++++++++
.../expected/pg_stat_statements.out | 147 ++++++++--------
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/utility.out | 142 ++++++++--------
.../pg_stat_statements--1.10--1.11.sql | 81 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/level_tracking.sql | 10 +-
.../pg_stat_statements/sql/oldextversions.sql | 7 +
.../sql/pg_stat_statements.sql | 28 +--
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/utility.sql | 26 +--
doc/src/sgml/pgstatstatements.sgml | 66 +++++++-
19 files changed, 848 insertions(+), 275 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 69fbc6a8580..89b9f2dda85 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
REGRESS = pg_stat_statements cursors utility level_tracking planning \
- cleanup oldextversions
+ 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
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 5d0dc196f97..b709bff830a 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT 3
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index c824ebdac5d..75f7edfe797 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 2c5bed841af..d2d21675c5e 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -134,7 +134,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -146,10 +146,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -270,7 +270,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -279,10 +279,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -302,7 +302,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-----------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -316,10 +316,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -395,7 +395,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -403,10 +403,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -449,7 +449,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -458,10 +458,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -474,8 +474,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -489,10 +489,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -508,9 +508,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -519,12 +519,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -538,11 +538,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -551,11 +551,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -568,12 +568,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -582,16 +582,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -603,16 +603,25 @@ DROP ROLE regress_stats_user2;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
-- FROM [ONLY]
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index dbb8f661c01..e89c8e9a3c9 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT 1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series(1,10) AS tab(a) WHERE a = 3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series(1, 10) AS tab(a) WHERE a < 5 AND a > 2
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 2::int AS col2 +
| | FROM generate_series(1, 10) AS tab(a) WHERE a < 5 AND a > 2
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series(1, 10) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,11 +543,11 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..782029727cc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,81 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index ad1fe444969..316bec69e0a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1370,11 +1377,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;
@@ -1438,18 +1457,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();
}
@@ -1462,7 +1497,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1474,6 +1510,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1604,6 +1650,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1681,6 +1731,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));
@@ -1749,6 +1801,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);
}
@@ -1836,6 +1890,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1844,6 +1903,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1952,6 +2012,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;
@@ -2515,11 +2577,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;
@@ -2527,6 +2608,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,
@@ -2536,6 +2618,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. */
@@ -2544,23 +2628,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2568,8 +2650,7 @@ 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);
}
}
}
@@ -2579,8 +2660,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2594,7 +2674,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;
@@ -2632,6 +2711,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 2f95bf98e5c..43a4d7b8c45 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,7 +91,7 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 3a3d2350667..d31055fd38f 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -127,7 +127,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -151,7 +151,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -180,7 +180,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -201,7 +201,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -212,27 +212,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -244,8 +244,12 @@ DROP ROLE regress_stats_user2;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
-- FROM [ONLY]
CREATE TABLE tbl_inh(id integer);
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 4c58a4c978e..a7f97c9a7d6 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..0354bb682c0 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -674,7 +707,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>
@@ -693,6 +727,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>
--
2.30.2
On Wed, 1 Mar 2023 at 04:04, Andrei Zubkov <zubkov@moonset.ru> wrote:
Hi!
I've attached a new version of a patch - rebase to the current master.
The CFBot (http://cfbot.cputube.org/) doesn't seem to like this. It
looks like all the Meson builds are failing, perhaps there's something
particular about the test environment that is either not set up right
or is exposing a bug?
Please check if this is a real failure or a cfbot failure.
--
Gregory Stark
As Commitfest Manager
Hi Gregory,
On Wed, 2023-03-01 at 14:24 -0500, Gregory Stark (as CFM) wrote:
The CFBot (http://cfbot.cputube.org/) doesn't seem to like this. It
looks like all the Meson builds are failing, perhaps there's
something
particular about the test environment that is either not set up right
or is exposing a bug?
Thank you, I've missed it.
Please check if this is a real failure or a cfbot failure.
It is my failure. Just forgot to update meson.build
I think CFBot should be happy now.
Regards,
--
Andrei Zubkov
Attachments:
v18-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v18-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 0d97c3d3bc0f00c3a1c2bf00cbe9d1aab7e90a32 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 1 Mar 2023 23:07:00 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../pg_stat_statements/expected/cursors.out | 28 +--
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/level_tracking.out | 72 ++++----
.../expected/oldextversions.out | 70 ++++++++
.../expected/pg_stat_statements.out | 147 ++++++++--------
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/utility.out | 142 ++++++++--------
contrib/pg_stat_statements/meson.build | 2 +
.../pg_stat_statements--1.10--1.11.sql | 81 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/level_tracking.sql | 10 +-
.../pg_stat_statements/sql/oldextversions.sql | 7 +
.../sql/pg_stat_statements.sql | 28 +--
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/utility.sql | 26 +--
doc/src/sgml/pgstatstatements.sgml | 66 +++++++-
20 files changed, 850 insertions(+), 275 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 69fbc6a8580..89b9f2dda85 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,7 @@ LDFLAGS_SL += $(filter -lm, $(LIBS))
REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf
REGRESS = pg_stat_statements cursors utility level_tracking planning \
- cleanup oldextversions
+ 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
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 5d0dc196f97..b709bff830a 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT 3
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index c824ebdac5d..75f7edfe797 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 2c5bed841af..d2d21675c5e 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -4,10 +4,10 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "int";
@@ -134,7 +134,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "int" | 2 | 2
SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2
SELECT $1 || $2 | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
WITH t(f) AS ( +| 1 | 2
VALUES ($1), ($2) +| |
@@ -146,10 +146,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -270,7 +270,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT * FROM test ORDER BY a | 1 | 12
SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4
SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
UPDATE test SET b = $1 WHERE a = $2 | 6 | 6
UPDATE test SET b = $1 WHERE a > $2 | 1 | 3
@@ -279,10 +279,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- utility "create table" should not be shown
@@ -302,7 +302,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
-----------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_test WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_test VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SELECT query, calls, rows, +| 0 | 0 | f | f | t
wal_bytes > $1 as wal_bytes_generated, +| | | | |
wal_records > $2 as wal_records_generated, +| | | | |
@@ -316,10 +316,10 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -395,7 +395,7 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
@@ -403,10 +403,10 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -449,7 +449,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -458,10 +458,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -474,8 +474,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 11
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -489,10 +489,10 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -508,9 +508,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 23
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -519,12 +519,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -538,11 +538,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 35
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -551,11 +551,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -568,12 +568,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 46
SET ROLE regress_stats_user2 | 1 | 0
@@ -582,16 +582,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
query | calls | rows
------------------------------------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 0 | 0
(2 rows)
@@ -603,16 +603,25 @@ DROP ROLE regress_stats_user2;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | f
(1 row)
-SELECT dealloc FROM pg_stat_statements_info;
- dealloc
----------
- 0
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+ dealloc | reset_after_ref
+---------+-----------------
+ 0 | t
+(1 row)
+
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
+ reset_ts_match
+----------------
+ t
(1 row)
-- FROM [ONLY]
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index dbb8f661c01..e89c8e9a3c9 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT 1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series(1,10) AS tab(a) WHERE a = 3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series(1, 10) AS tab(a) WHERE a < 5 AND a > 2
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 2::int AS col2 +
| | FROM generate_series(1, 10) AS tab(a) WHERE a < 5 AND a > 2
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series(1, 10) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,11 +543,11 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 10ccc263000..f7426a6b61e 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
@@ -45,6 +46,7 @@ tests += {
'utility',
'level_tracking',
'planning',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..782029727cc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,81 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index ad1fe444969..316bec69e0a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1370,11 +1377,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;
@@ -1438,18 +1457,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();
}
@@ -1462,7 +1497,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1474,6 +1510,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1604,6 +1650,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1681,6 +1731,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));
@@ -1749,6 +1801,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);
}
@@ -1836,6 +1890,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1844,6 +1903,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1952,6 +2012,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;
@@ -2515,11 +2577,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;
@@ -2527,6 +2608,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,
@@ -2536,6 +2618,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. */
@@ -2544,23 +2628,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2568,8 +2650,7 @@ 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);
}
}
}
@@ -2579,8 +2660,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2594,7 +2674,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;
@@ -2632,6 +2711,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 2f95bf98e5c..43a4d7b8c45 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,7 +91,7 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 3a3d2350667..d31055fd38f 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -5,7 +5,7 @@ CREATE EXTENSION pg_stat_statements;
--
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "int";
@@ -57,7 +57,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- CRUD: INSERT SELECT UPDATE DELETE on test table
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TEMP TABLE test (a int, b char(20));
@@ -127,7 +127,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- INSERT, UPDATE, DELETE on test table to validate WAL generation metrics
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- utility "create table" should not be shown
CREATE TABLE pgss_test (a int, b char(20));
@@ -151,7 +151,7 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -180,7 +180,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -201,7 +201,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -212,27 +212,27 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1)) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -244,8 +244,12 @@ DROP ROLE regress_stats_user2;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
-SELECT dealloc FROM pg_stat_statements_info;
+SELECT now() AS ref_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+SELECT pg_stat_statements_reset() AS stats_reset_ts \gset
+SELECT dealloc, stats_reset >= :'ref_ts' AS reset_after_ref FROM pg_stat_statements_info;
+-- check stats_reset timestamp
+SELECT stats_reset = :'stats_reset_ts' AS reset_ts_match FROM pg_stat_statements_info;
-- FROM [ONLY]
CREATE TABLE tbl_inh(id integer);
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 4c58a4c978e..a7f97c9a7d6 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..0354bb682c0 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -674,7 +707,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>
@@ -693,6 +727,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>
--
2.30.2
I'm sorry, It seems this is failing again? It's Makefile and
meson.build again :(
Though there are also
patching file contrib/pg_stat_statements/sql/oldextversions.sql
can't find file to patch at input line 1833
and
|--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
|+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
--------------------------
No file to patch. Skipping patch.
--
Gregory Stark
As Commitfest Manager
Hi Gregory,
patching file contrib/pg_stat_statements/sql/oldextversions.sql
can't find file to patch at input line 1833and
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql +++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql--------------------------
No file to patch. Skipping patch.
Thank you for your attention.
Yes, it is due to parallel work on "Normalization of utility queries in
pg_stat_statements" patch
(/messages/by-id/Y/7Y9U/y/keAW3qH@paquier.xyz)
It seems I've found something strange in new test files - I've
mentioned this in a thread of a patch. Once there will be any solution
I'll do a rebase again as soon as possible.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Hi,
I've done a rebase of a patch to the current master.
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v19-0001-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v19-0001-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From e43e9eab39bbd377665a7cad3b7fe7162f8f6578 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <a.zubkov@postgrespro.ru>
Date: Sat, 11 Mar 2023 09:53:10 +0300
Subject: [PATCH] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion:
https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 20 +--
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/level_tracking.out | 80 ++++-----
.../expected/oldextversions.out | 70 ++++++++
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 ++++++-------
.../pg_stat_statements/expected/utility.out | 150 ++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/meson.build | 2 +
.../pg_stat_statements--1.10--1.11.sql | 81 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 4 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
.../pg_stat_statements/sql/oldextversions.sql | 7 +
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 28 +--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
doc/src/sgml/pgstatstatements.sgml | 66 +++++++-
26 files changed, 881 insertions(+), 314 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 5578a9dd4e3..b2446e7a1cf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,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
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index 7b9c8f979ee..4c723a038b9 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,12 +136,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index 0047aba5d1a..ccb0edd26cc 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,20 +543,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 3e3062ada9c..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
@@ -48,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..782029727cc
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,81 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5285c3f7faa..a5ef53c42c4 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1372,11 +1379,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;
@@ -1440,18 +1459,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();
}
@@ -1464,7 +1499,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1476,6 +1512,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1606,6 +1652,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1683,6 +1733,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));
@@ -1751,6 +1803,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);
}
@@ -1838,6 +1892,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1846,6 +1905,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1954,6 +2014,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;
@@ -2517,11 +2579,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;
@@ -2529,6 +2610,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,
@@ -2538,6 +2620,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. */
@@ -2546,23 +2630,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2570,8 +2652,7 @@ 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);
}
}
}
@@ -2581,8 +2662,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2596,7 +2676,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;
@@ -2634,6 +2713,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index af2f9fcf73b..49d1b3a2701 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -73,4 +73,4 @@ MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a
DROP TABLE pgss_dml_tab;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 225d30a62a6..2d9b2684ebd 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -276,4 +276,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..0354bb682c0 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -674,7 +707,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>
@@ -693,6 +727,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>
--
2.30.2
On Sat, Mar 11, 2023 at 02:49:50PM +0300, Andrei Zubkov wrote:
Hi,
I've done a rebase of a patch to the current master.
+/* 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);
+ALTER EXTENSION pg_stat_statements DROP FUNCTION
+ pg_stat_statements_reset(Oid, Oid, bigint);
The upgrade script of an extension is launched by the backend in the
context of an extension, so these three queries should not be needed,
as far as I recall.
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
Wouldn't it be better to do this kind of refactoring in its own patch
to make the follow-up changes more readable? This function is changed
to return a timestamp rather than void, but IS NOT NULL applied on the
existing queries would also return true. This would clean up quite a
few diffs in the main patch..
--
Michael
Hi Michael,
Thank you for your attention.
On Thu, 2023-03-16 at 16:13 +0900, Michael Paquier wrote:
+/* 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); +ALTER EXTENSION pg_stat_statements DROP FUNCTION + pg_stat_statements_reset(Oid, Oid, bigint);The upgrade script of an extension is launched by the backend in the
context of an extension, so these three queries should not be needed,
as far as I recall.
Agreed. I've done it as it was in previous versions. But I'm sure those
are unnecessary.
Wouldn't it be better to do this kind of refactoring in its own patch
to make the follow-up changes more readable? This function is
changed
to return a timestamp rather than void, but IS NOT NULL applied on
the
existing queries would also return true. This would clean up quite a
few diffs in the main patch..
Splitting this commit seems reasonable to me.
New version is attached.
Attachments:
v20-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v20-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From 52e75fa05f5dea5700d96aea81ea81d91492b018 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 16 Mar 2023 13:18:59 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 ++--
contrib/pg_stat_statements/expected/dml.out | 20 +--
.../expected/level_tracking.out | 80 +++++-----
.../pg_stat_statements/expected/planning.out | 18 +--
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 +++++++-------
.../pg_stat_statements/expected/utility.out | 150 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 4 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 28 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 278 insertions(+), 273 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index 7b9c8f979ee..4c723a038b9 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,12 +136,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index 0047aba5d1a..ccb0edd26cc 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,20 +543,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index af2f9fcf73b..49d1b3a2701 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -73,4 +73,4 @@ MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a
DROP TABLE pgss_dml_tab;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 225d30a62a6..2d9b2684ebd 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -276,4 +276,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.30.2
v20-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v20-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 1c5b91eef4d6104b91190b0c2f703049265f9602 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 16 Mar 2023 13:46:10 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 70 ++++++++
contrib/pg_stat_statements/meson.build | 2 +
.../pg_stat_statements--1.10--1.11.sql | 75 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 7 +
doc/src/sgml/pgstatstatements.sgml | 66 +++++++-
10 files changed, 597 insertions(+), 41 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 5578a9dd4e3..b2446e7a1cf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 3e3062ada9c..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
@@ -48,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..1bf57dd06d3
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,75 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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
+
+/* Then we can drop them */
+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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5285c3f7faa..a5ef53c42c4 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1372,11 +1379,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;
@@ -1440,18 +1459,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();
}
@@ -1464,7 +1499,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1476,6 +1512,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1606,6 +1652,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1683,6 +1733,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));
@@ -1751,6 +1803,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);
}
@@ -1838,6 +1892,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1846,6 +1905,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1954,6 +2014,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;
@@ -2517,11 +2579,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;
@@ -2529,6 +2610,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,
@@ -2538,6 +2620,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. */
@@ -2546,23 +2630,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2570,8 +2652,7 @@ 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);
}
}
}
@@ -2581,8 +2662,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2596,7 +2676,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;
@@ -2634,6 +2713,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..0354bb682c0 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -674,7 +707,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>
@@ -693,6 +727,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>
--
2.30.2
A little comment fix in update script of a patch
--
Andrei Zubkov
Attachments:
v21-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v21-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From 52e75fa05f5dea5700d96aea81ea81d91492b018 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 16 Mar 2023 13:18:59 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 ++--
contrib/pg_stat_statements/expected/dml.out | 20 +--
.../expected/level_tracking.out | 80 +++++-----
.../pg_stat_statements/expected/planning.out | 18 +--
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 +++++++-------
.../pg_stat_statements/expected/utility.out | 150 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 4 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 28 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 278 insertions(+), 273 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index 7b9c8f979ee..4c723a038b9 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,12 +136,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index 0047aba5d1a..ccb0edd26cc 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,20 +543,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index af2f9fcf73b..49d1b3a2701 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -73,4 +73,4 @@ MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a
DROP TABLE pgss_dml_tab;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 225d30a62a6..2d9b2684ebd 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -276,4 +276,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.30.2
v21-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v21-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 092089067ccc8ed1b3baab8bc9d10cad82618b6e Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 16 Mar 2023 15:27:03 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 70 ++++++++
contrib/pg_stat_statements/meson.build | 2 +
.../pg_stat_statements--1.10--1.11.sql | 75 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 7 +
doc/src/sgml/pgstatstatements.sgml | 66 +++++++-
10 files changed, 597 insertions(+), 41 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 5578a9dd4e3..b2446e7a1cf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 3e3062ada9c..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
@@ -48,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..41ff4bbc61b
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,75 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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
+
+/* 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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+CREATE VIEW pg_stat_statements AS
+ SELECT * FROM pg_stat_statements(true);
+
+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;
+
+GRANT SELECT ON pg_stat_statements TO PUBLIC;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5285c3f7faa..a5ef53c42c4 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1372,11 +1379,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;
@@ -1440,18 +1459,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();
}
@@ -1464,7 +1499,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1476,6 +1512,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1606,6 +1652,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1683,6 +1733,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));
@@ -1751,6 +1803,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);
}
@@ -1838,6 +1892,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1846,6 +1905,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1954,6 +2014,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;
@@ -2517,11 +2579,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;
@@ -2529,6 +2610,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,
@@ -2538,6 +2620,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. */
@@ -2546,23 +2630,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2570,8 +2652,7 @@ 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);
}
}
}
@@ -2581,8 +2662,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2596,7 +2676,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;
@@ -2634,6 +2713,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..0354bb682c0 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,25 @@
Total time spent by the statement on emitting code, 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
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -674,7 +707,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>
@@ -693,6 +727,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>
--
2.30.2
Hello!
The documentation still describes the function pg_stat_statements_reset like this
By default, this function can only be executed by superusers.
But unfortunately, this part was lost somewhere.
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_statements_reset(Oid, Oid, bigint, boolean) FROM PUBLIC;
should be added to the upgrade script
Also, shouldn't we first do:
/* First we have to remove them from the extension */
ALTER EXTENSION pg_stat_statements DROP VIEW ..
ALTER EXTENSION pg_stat_statements DROP FUNCTION ..
like in previous upgrade scripts?
+ Time at which min/max statistics gathering started for this + statement
I think it would be better to explicitly mention in the documentation all 4 fields for which minmax_stats_since displays the time.
regards, Sergei
Hi Sergei!
Thank you for your review.
On Tue, 2023-03-21 at 23:18 +0300, Sergei Kornilov wrote:
-- Don't want this to be available to non-superusers.
REVOKE ALL ON FUNCTION pg_stat_statements_reset(Oid, Oid, bigint,
boolean) FROM PUBLIC;should be added to the upgrade script
Indeed.
Also, shouldn't we first do:
/* First we have to remove them from the extension */
ALTER EXTENSION pg_stat_statements DROP VIEW ..
ALTER EXTENSION pg_stat_statements DROP FUNCTION ..like in previous upgrade scripts?
It was discussed few messages earlier in this thread. We've decided
that those are unnecessary in upgrade script.
+ Time at which min/max statistics gathering started for this + statementI think it would be better to explicitly mention in the documentation
all 4 fields for which minmax_stats_since displays the time.
Agreed.
New version is attached.
regards, Andrei
Attachments:
v22-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v22-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From 187aeb26df54daea4f38eb9a8605ee09985b55ae Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 22 Mar 2023 10:09:58 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush, Michael Paquier, Sergei Kornilov
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 ++--
contrib/pg_stat_statements/expected/dml.out | 20 +--
.../expected/level_tracking.out | 80 +++++-----
.../pg_stat_statements/expected/planning.out | 18 +--
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 +++++++-------
.../pg_stat_statements/expected/utility.out | 150 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 4 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 28 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 278 insertions(+), 273 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index 7b9c8f979ee..4c723a038b9 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,12 +136,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index 0047aba5d1a..ccb0edd26cc 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -230,7 +230,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -246,10 +246,10 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -257,21 +257,21 @@ CALL sum_one(199);
CALL sum_two(1,1);
CALL sum_two(1,2);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | CALL sum_one(199)
1 | 0 | CALL sum_one(3)
1 | 0 | CALL sum_two(1,1)
1 | 0 | CALL sum_two(1,2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -301,14 +301,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -334,13 +334,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -360,13 +360,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -388,13 +388,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -409,13 +409,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -441,14 +441,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -460,10 +460,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -521,16 +521,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -543,20 +543,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index af2f9fcf73b..49d1b3a2701 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -73,4 +73,4 @@ MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a
DROP TABLE pgss_dml_tab;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 225d30a62a6..2d9b2684ebd 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -137,7 +137,7 @@ DECLARE
BEGIN
SELECT (i + j)::int INTO r;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -146,7 +146,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -158,7 +158,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -175,7 +175,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -188,7 +188,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -200,7 +200,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -208,7 +208,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -234,7 +234,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -263,7 +263,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -276,4 +276,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.30.2
v22-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v22-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From f9b2d3128e3efeb52e6b9306d3ce98e661645520 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 22 Mar 2023 10:11:48 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Reviewed by: Julien Rouhaud, Hayato Kuroda, Yuki Seino, Chengxi Sun,
Anton Melnikov, Darren Rush, Michael Paquier, Sergei Kornilov
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 4 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 70 ++++++++
contrib/pg_stat_statements/meson.build | 2 +
.../pg_stat_statements--1.10--1.11.sql | 78 +++++++++
.../pg_stat_statements/pg_stat_statements.c | 139 +++++++++++----
.../pg_stat_statements.control | 2 +-
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 7 +
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
10 files changed, 603 insertions(+), 41 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index 5578a9dd4e3..b2446e7a1cf 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -6,7 +6,7 @@ OBJS = \
pg_stat_statements.o
EXTENSION = pg_stat_statements
-DATA = pg_stat_statements--1.4.sql \
+DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.10--1.11.sql \
pg_stat_statements--1.9--1.10.sql pg_stat_statements--1.8--1.9.sql \
pg_stat_statements--1.7--1.8.sql pg_stat_statements--1.6--1.7.sql \
pg_stat_statements--1.5--1.6.sql pg_stat_statements--1.4--1.5.sql \
@@ -18,7 +18,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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index efb2049ecff..1e1cc11a8e2 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -250,4 +250,74 @@ SELECT count(*) > 0 AS has_data FROM pg_stat_statements;
t
(1 row)
+-- 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 | | |
+ blk_read_time | double precision | | |
+ 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 | | |
+ 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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 3e3062ada9c..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -21,6 +21,7 @@ contrib_targets += pg_stat_statements
install_data(
'pg_stat_statements.control',
'pg_stat_statements--1.4.sql',
+ 'pg_stat_statements--1.10--1.11.sql',
'pg_stat_statements--1.9--1.10.sql',
'pg_stat_statements--1.8--1.9.sql',
'pg_stat_statements--1.7--1.8.sql',
@@ -48,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
new file mode 100644
index 00000000000..7b80b6f3678
--- /dev/null
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -0,0 +1,78 @@
+/* contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql */
+
+-- 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
+
+/* 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 userid oid,
+ OUT dbid oid,
+ OUT toplevel bool,
+ OUT queryid bigint,
+ OUT query text,
+ OUT plans int8,
+ OUT total_plan_time float8,
+ OUT min_plan_time float8,
+ OUT max_plan_time float8,
+ OUT mean_plan_time float8,
+ OUT stddev_plan_time float8,
+ OUT calls int8,
+ OUT total_exec_time float8,
+ OUT min_exec_time float8,
+ OUT max_exec_time float8,
+ OUT mean_exec_time float8,
+ OUT stddev_exec_time float8,
+ OUT rows int8,
+ OUT shared_blks_hit int8,
+ OUT shared_blks_read int8,
+ OUT shared_blks_dirtied int8,
+ OUT shared_blks_written int8,
+ OUT local_blks_hit int8,
+ OUT local_blks_read int8,
+ OUT local_blks_dirtied int8,
+ OUT local_blks_written int8,
+ OUT temp_blks_read int8,
+ OUT temp_blks_written int8,
+ OUT blk_read_time float8,
+ OUT blk_write_time float8,
+ OUT temp_blk_read_time float8,
+ OUT temp_blk_write_time float8,
+ OUT wal_records int8,
+ OUT wal_fpi int8,
+ OUT wal_bytes numeric,
+ OUT jit_functions int8,
+ OUT jit_generation_time float8,
+ OUT jit_inlining_count int8,
+ OUT jit_inlining_time float8,
+ OUT jit_optimization_count int8,
+ OUT jit_optimization_time float8,
+ OUT jit_emission_count int8,
+ OUT jit_emission_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'
+LANGUAGE C STRICT VOLATILE PARALLEL SAFE;
+
+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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 5285c3f7faa..a5ef53c42c4 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -84,7 +84,7 @@ PG_MODULE_MAGIC;
#define PGSS_TEXT_FILE PG_STAT_TMP_DIR "/pgss_query_texts.stat"
/* Magic number identifying the stats file format */
-static const uint32 PGSS_FILE_HEADER = 0x20220408;
+static const uint32 PGSS_FILE_HEADER = 0x20230124;
/* PostgreSQL major version number, changes in which invalidate all entries */
static const uint32 PGSS_PG_MAJOR_VERSION = PG_VERSION_NUM / 100;
@@ -118,7 +118,8 @@ typedef enum pgssVersion
PGSS_V1_3,
PGSS_V1_8,
PGSS_V1_9,
- PGSS_V1_10
+ PGSS_V1_10,
+ PGSS_V1_11
} pgssVersion;
typedef enum pgssStoreKind
@@ -162,9 +163,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
@@ -225,6 +226,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,11 +311,13 @@ 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);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_9);
PG_FUNCTION_INFO_V1(pg_stat_statements_1_10);
+PG_FUNCTION_INFO_V1(pg_stat_statements_1_11);
PG_FUNCTION_INFO_V1(pg_stat_statements);
PG_FUNCTION_INFO_V1(pg_stat_statements_info);
@@ -358,7 +363,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,
@@ -653,6 +658,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 */
@@ -1372,11 +1379,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;
@@ -1440,18 +1459,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();
}
@@ -1464,7 +1499,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 43 /* maximum of above */
+#define PG_STAT_STATEMENTS_COLS_V1_11 45
+#define PG_STAT_STATEMENTS_COLS 45 /* maximum of above */
/*
* Retrieve statement statistics.
@@ -1476,6 +1512,16 @@ pg_stat_statements_reset(PG_FUNCTION_ARGS)
* expected API version is identified by embedding it in the C name of the
* function. Unfortunately we weren't bright enough to do that for 1.1.
*/
+Datum
+pg_stat_statements_1_11(PG_FUNCTION_ARGS)
+{
+ bool showtext = PG_GETARG_BOOL(0);
+
+ pg_stat_statements_internal(fcinfo, PGSS_V1_11, showtext);
+
+ return (Datum) 0;
+}
+
Datum
pg_stat_statements_1_10(PG_FUNCTION_ARGS)
{
@@ -1606,6 +1652,10 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
if (api_version != PGSS_V1_10)
elog(ERROR, "incorrect number of output arguments");
break;
+ case PG_STAT_STATEMENTS_COLS_V1_11:
+ if (api_version != PGSS_V1_11)
+ elog(ERROR, "incorrect number of output arguments");
+ break;
default:
elog(ERROR, "incorrect number of output arguments");
}
@@ -1683,6 +1733,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));
@@ -1751,6 +1803,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);
}
@@ -1838,6 +1892,11 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
values[i++] = Int64GetDatumFast(tmp.jit_emission_count);
values[i++] = Float8GetDatumFast(tmp.jit_emission_time);
}
+ if (api_version >= PGSS_V1_11)
+ {
+ values[i++] = TimestampTzGetDatum(stats_since);
+ values[i++] = TimestampTzGetDatum(minmax_stats_since);
+ }
Assert(i == (api_version == PGSS_V1_0 ? PG_STAT_STATEMENTS_COLS_V1_0 :
api_version == PGSS_V1_1 ? PG_STAT_STATEMENTS_COLS_V1_1 :
@@ -1846,6 +1905,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo,
api_version == PGSS_V1_8 ? PG_STAT_STATEMENTS_COLS_V1_8 :
api_version == PGSS_V1_9 ? PG_STAT_STATEMENTS_COLS_V1_9 :
api_version == PGSS_V1_10 ? PG_STAT_STATEMENTS_COLS_V1_10 :
+ api_version == PGSS_V1_11 ? PG_STAT_STATEMENTS_COLS_V1_11 :
-1 /* fail if you forget to update this assert */ ));
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
@@ -1954,6 +2014,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;
@@ -2517,11 +2579,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;
@@ -2529,6 +2610,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,
@@ -2538,6 +2620,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. */
@@ -2546,23 +2630,21 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.dbid = dbid;
key.queryid = queryid;
- /* Remove the key if it exists, starting with the top-level entry */
+ /* Reset is started from nested-level */
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 entries for top level statements */
+ SINGLE_ENTRY_RESET(entry);
+
+ /* Reset entries for top level statements */
key.toplevel = true;
+ entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
- /* Remove the key if exists */
- entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_REMOVE, NULL);
- if (entry) /* found */
- num_remove++;
+ 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)
{
@@ -2570,8 +2652,7 @@ 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);
}
}
}
@@ -2581,8 +2662,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2596,7 +2676,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;
@@ -2634,6 +2713,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control
index 0747e481383..8a76106ec67 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.control
+++ b/contrib/pg_stat_statements/pg_stat_statements.control
@@ -1,5 +1,5 @@
# pg_stat_statements extension
comment = 'track planning and execution statistics of all SQL statements executed'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pg_stat_statements'
relocatable = true
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index e2a83106d4c..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -48,4 +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 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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b1214ee6453..17a9913915b 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
@@ -473,6 +487,28 @@
Total time spent by the statement on emitting code, 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>
@@ -674,7 +710,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>
@@ -693,6 +730,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>
--
2.30.2
On 22 Mar 2023, at 09:17, Andrei Zubkov <zubkov@moonset.ru> wrote:
New version is attached.
This patch is marked RfC but didn't get reviewed/committed during this CF so
I'm moving it to the next, the patch no longer applies though so please submit
an updated version.
--
Daniel Gustafsson
Hi hackers,
New version 23 attached. It contains rebase to the current master.
Noted that v1.11 adds new fields to the pg_stat_sstatements view, but
leaves the PGSS_FILE_HEADER constant unchanged. It this correct?
--
Andrei Zubkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
Attachments:
v23-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v23-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From 1bc4ccaed7cd3922d0b0bb83afd07f386792c8dc Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 19 Oct 2023 15:04:42 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 28 +--
.../expected/level_tracking.out | 80 ++++----
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++--
.../expected/user_activity.out | 120 +++++------
.../pg_stat_statements/expected/utility.out | 192 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 6 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 34 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 307 insertions(+), 302 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index ede47a71acc..f6ac8da5ca2 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,16 +136,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
@@ -166,9 +166,9 @@ FROM pg_stat_statements;
t | t | t | t
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index cc6e898cdf5..07502231259 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Two-phase transactions
@@ -205,19 +205,19 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
1 | 0 | COMMIT PREPARED $1
2 | 0 | PREPARE TRANSACTION $1
1 | 0 | ROLLBACK PREPARED $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Savepoints
@@ -235,20 +235,20 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | BEGIN
1 | 0 | COMMIT
3 | 0 | RELEASE $1
4 | 0 | ROLLBACK TO $1
4 | 0 | SAVEPOINT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -284,7 +284,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -321,10 +321,10 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -346,22 +346,22 @@ CALL in_out(2, 1, 2);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | CALL in_out($1, $2, $3)
1 | 0 | CALL overload($1)
1 | 0 | CALL overload($1)
2 | 0 | CALL sum_one($1)
2 | 0 | CALL sum_two($1,$2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -391,14 +391,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -424,13 +424,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -450,13 +450,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -478,13 +478,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -499,13 +499,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Execution statements
@@ -534,19 +534,19 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+---------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -572,14 +572,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -591,10 +591,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -652,16 +652,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -674,20 +674,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index 3b5d2afb858..9986b0a22d3 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -77,7 +77,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
INSERT INTO pgss_extend_temp_tab (a, b) SELECT generate_series(1, 1000), 'something';
WITH sizes AS (
@@ -92,4 +92,4 @@ SELECT
SUM(shared_blks_dirtied) >= (SELECT rel_size FROM sizes) AS dirtied_ok
FROM pg_stat_statements;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 04598e5ae46..83d1894a092 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Two-phase transactions
BEGIN;
@@ -123,7 +123,7 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Savepoints
BEGIN;
@@ -140,7 +140,7 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -185,7 +185,7 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -198,7 +198,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -210,7 +210,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -227,7 +227,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -240,7 +240,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -252,7 +252,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -260,7 +260,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Execution statements
SELECT 1 as a;
@@ -273,7 +273,7 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -299,7 +299,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -328,7 +328,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -341,4 +341,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.39.2
v23-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v23-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 7d4c30b3775f0cdc2afb0104768bad1399c28809 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Thu, 19 Oct 2023 15:07:07 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 116 +++++++------
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 23 ++-
.../pg_stat_statements/pg_stat_statements.c | 116 ++++++++++---
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 4 +-
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
9 files changed, 509 insertions(+), 95 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index eba4a95d91a..aecd1d6a2a0 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5a5404bbd57..ec317b0d6be 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 15b7c7f2b02..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
index 5fe211184bf..0bb2c397711 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index baff87b49e1..94d86dabd8d 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -162,9 +162,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
@@ -235,6 +235,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;
@@ -318,6 +320,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);
@@ -369,7 +372,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,
@@ -664,6 +667,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 */
@@ -1380,11 +1385,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;
@@ -1454,18 +1471,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();
}
@@ -1478,8 +1511,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.
@@ -1712,6 +1745,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));
@@ -1780,6 +1815,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);
}
@@ -1876,6 +1913,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 :
@@ -1994,6 +2033,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;
@@ -2557,11 +2598,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;
@@ -2569,6 +2629,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,
@@ -2578,6 +2639,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. */
@@ -2587,22 +2650,22 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
key.queryid = queryid;
/*
- * Remove the key if it exists, starting with the non-top-level entry.
+ * Remove 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)
{
@@ -2610,8 +2673,7 @@ 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);
}
}
}
@@ -2621,8 +2683,7 @@ entry_reset(Oid userid, Oid dbid, uint64 queryid)
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);
}
}
@@ -2636,7 +2697,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;
@@ -2674,6 +2734,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 2435c0c576e..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 6c7ca962f81..15c589432e5 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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 @@
Total time spent by the statement on emitting code, 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>
--
2.39.2
Hi,
During last moving to the current commitfest this patch have lost its
reviewers list. With respect to reviewers contribution in this patch, I
think reviewers list should be fixed.
regards,
Andrei Zubkov
Postgres Professional
The Russian Postgres Company
On 24.10.23 09:58, Andrei Zubkov wrote:
During last moving to the current commitfest this patch have lost its
reviewers list. With respect to reviewers contribution in this patch, I
think reviewers list should be fixed.
I don't think it's the purpose of the commitfest app to track how *has*
reviewed a patch. The purpose is to plan and allocate *current* work.
If we keep a bunch of reviewers listed on a patch who are not actually
reviewing the patch, then that effectively blocks new reviewers from
signing up and the patch will not make progress.
Past reviewers should of course be listed in the commit message, the
release notes, etc. as appropriate.
On Tue, Oct 24, 2023 at 6:57 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 24.10.23 09:58, Andrei Zubkov wrote:
During last moving to the current commitfest this patch have lost its
reviewers list. With respect to reviewers contribution in this patch, I
think reviewers list should be fixed.I don't think it's the purpose of the commitfest app to track how *has*
reviewed a patch. The purpose is to plan and allocate *current* work.
If we keep a bunch of reviewers listed on a patch who are not actually
reviewing the patch, then that effectively blocks new reviewers from
signing up and the patch will not make progress.Past reviewers should of course be listed in the commit message, the
release notes, etc. as appropriate.
Really? Last time this topic showed up at least one committer said
that they tend to believe the CF app more than digging the thread [1]/messages/by-id/552155.1648737431@sss.pgh.pa.us,
and some other hackers mentioned other usage for being kept in the
reviewer list. Since no progress has been made on the CF app since
I'm not sure it's the best idea to drop reviewer names from patch
entries generally.
On 24.10.23 14:40, Julien Rouhaud wrote:
On Tue, Oct 24, 2023 at 6:57 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 24.10.23 09:58, Andrei Zubkov wrote:
During last moving to the current commitfest this patch have lost its
reviewers list. With respect to reviewers contribution in this patch, I
think reviewers list should be fixed.I don't think it's the purpose of the commitfest app to track how *has*
reviewed a patch. The purpose is to plan and allocate *current* work.
If we keep a bunch of reviewers listed on a patch who are not actually
reviewing the patch, then that effectively blocks new reviewers from
signing up and the patch will not make progress.Past reviewers should of course be listed in the commit message, the
release notes, etc. as appropriate.Really? Last time this topic showed up at least one committer said
that they tend to believe the CF app more than digging the thread [1],
and some other hackers mentioned other usage for being kept in the
reviewer list. Since no progress has been made on the CF app since
I'm not sure it's the best idea to drop reviewer names from patch
entries generally.
There is a conflict between the two purposes. But it is clearly the
case that reviewers will more likely pick up patches that have no
reviewers assigned. So if you keep stale reviewer entries around, then
a patch that stays around for a while will never get reviewed again. I
think this is a significant problem at the moment, and I made it part of
my mission during the last commitfest to clean it up. If people want to
put the stale reviewer entries back in, that is possible, but I would
caution against that, because that would just self-sabotage those patches.
On 19/10/2023 19:40, Andrei Zubkov wrote:
Hi hackers,
New version 23 attached. It contains rebase to the current master.
I discovered the patch and parameters you've proposed. In my opinion,
the stats_since parameter adds valuable information and should
definitely be included in the stats data because the statement can be
noteless removed from the list and inserted again.
But minmax_stats_since and changes in the UI of the reset routine look
like syntactic sugar here.
I can't convince myself that it is really needed. Do you have some set
of cases that can enforce the changes proposed? Maybe we should
intensively work on adding the 'stats_since' parameter and continue the
discussion of the necessity of another one?
--
regards,
Andrei Lepikhov
Postgres Professional
Hi Andrei,
On Wed, 2023-10-25 at 13:59 +0700, Andrei Lepikhov wrote:
But minmax_stats_since and changes in the UI of the reset routine
look like syntactic sugar here.
I can't convince myself that it is really needed. Do you have some
set of cases that can enforce the changes proposed?
Yes, it looks strange, but it is needed in some way.
The main purpose of this patch is to provide means for sampling
solutions for collecting statistics data in series of samples. The
first goal here - is to be sure that the statement was not evicted and
come back between samples (especially between rare samples). This is
the target of the stats_since field. The second goal - is the ability
of getting all statistic increments for the interval between samples.
All statistics provided by pg_stat_statements with except of min/max
values can be calculated for the interval since the last sample knowing
the values in the last and current samples. We need a way to get
min/max values too. This is achieved by min/max reset performed by the
sampling solution. The minmax_stats_since field is here for the same
purpose - the sampling solution is need to be sure that no one have
done a min/max reset between samples. And if such reset was performed
at least we know when it was performed.
regards,
Andrei Zubkov
Postgres Professional
On 19.10.2023 15:40, Andrei Zubkov wrote:
Hi hackers,
New version 23 attached. It contains rebase to the current master.
Noted that v1.11 adds new fields to the pg_stat_sstatements view, but
leaves the PGSS_FILE_HEADER constant unchanged. It this correct?
Hi! Thank you for your work on the subject.
1. I didn't understand why we first try to find pgssEntry with a false
top_level value, and later find it with a true top_level value.
/*
* Remove the entry if it exists, starting with the non-top-level entry.
*/
*key.toplevel = false;*
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
SINGLE_ENTRY_RESET(entry);
*/* Also reset the top-level entry if it exists. */
key.toplevel = true;*
entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
SINGLE_ENTRY_RESET(entry);
I looked through this topic and found some explanation in this email
[0]: , but I didn't understand it. Can you explain it to me?
2. And honestly, I think you should change
"Remove the entry if it exists, starting with the non-top-level entry." on
"Remove the entry or reset min/max time statistic information and the
timestamp if it exists, starting with the non-top-level entry."
And the same with the comment bellow:
"Also reset the top-level entry if it exists."
"Also remove the entry or reset min/max time statistic information and
the timestamp if it exists."
In my opinion, this is necessary because the minmax_only parameter is
set by the user, so both ways are possible.
0 -
/messages/by-id/62d16845-e74e-a6f9-9661-022e44f48922@inbox.ru
--
Regards,
Alena Rybakina
Hi Alena,
On Wed, 2023-10-25 at 16:25 +0300, Alena Rybakina wrote:
Hi! Thank you for your work on the subject.
1. I didn't understand why we first try to find pgssEntry with a
false top_level value, and later find it with a true top_level value.
In case of pg_stat_statements the top_level field is the bool field of
the pgssHashKey struct used as the key for pgss_hash hashtable. When we
are performing a reset only userid, dbid and queryid values are
provided. We need to reset both top-level and non-top level entries. We
have only one way to get them all from a hashtable - search for entries
having top_level=true and with top_level=false in their keys.
2. And honestly, I think you should change
"Remove the entry if it exists, starting with the non-top-level
entry." on
"Remove the entry or reset min/max time statistic information and
the timestamp if it exists, starting with the non-top-level entry."
And the same with the comment bellow:
"Also reset the top-level entry if it exists."
"Also remove the entry or reset min/max time statistic information
and the timestamp if it exists."
There are four such places actually - every time when the
SINGLE_ENTRY_RESET macro is used. The logic of reset performed is
described a bit in this macro definition. It seems quite redundant to
repeat this description four times. But I've noted that "remove" terms
should be replaced by "reset".
I've attached v24 with commits updated.
regards, Andrei Zubkov
Postgres Professional
Attachments:
v24-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v24-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From 4fadc88d5e6c1afe7558393ba99c28d070ac7244 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 25 Oct 2023 17:58:57 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 28 +--
.../expected/level_tracking.out | 80 ++++----
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++--
.../expected/user_activity.out | 120 +++++------
.../pg_stat_statements/expected/utility.out | 192 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 6 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 34 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 307 insertions(+), 302 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index ede47a71acc..f6ac8da5ca2 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,16 +136,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
@@ -166,9 +166,9 @@ FROM pg_stat_statements;
t | t | t | t
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index d924c87b41e..bad20e7f176 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -111,19 +111,19 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -165,13 +165,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -179,10 +179,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -202,9 +202,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index cc6e898cdf5..07502231259 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Two-phase transactions
@@ -205,19 +205,19 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
1 | 0 | COMMIT PREPARED $1
2 | 0 | PREPARE TRANSACTION $1
1 | 0 | ROLLBACK PREPARED $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Savepoints
@@ -235,20 +235,20 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | BEGIN
1 | 0 | COMMIT
3 | 0 | RELEASE $1
4 | 0 | ROLLBACK TO $1
4 | 0 | SAVEPOINT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -284,7 +284,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -321,10 +321,10 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -346,22 +346,22 @@ CALL in_out(2, 1, 2);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | CALL in_out($1, $2, $3)
1 | 0 | CALL overload($1)
1 | 0 | CALL overload($1)
2 | 0 | CALL sum_one($1)
2 | 0 | CALL sum_two($1,$2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -391,14 +391,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -424,13 +424,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -450,13 +450,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -478,13 +478,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -499,13 +499,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Execution statements
@@ -534,19 +534,19 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+---------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -572,14 +572,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -591,10 +591,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -652,16 +652,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -674,20 +674,20 @@ SET enable_seqscan = off;
SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET enable_seqscan = off
1 | 0 | SET enable_seqscan = on
2 | 0 | SET work_mem = '1MB'
1 | 0 | SET work_mem = '2MB'
(7 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index 3b5d2afb858..9986b0a22d3 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -77,7 +77,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
INSERT INTO pgss_extend_temp_tab (a, b) SELECT generate_series(1, 1000), 'something';
WITH sizes AS (
@@ -92,4 +92,4 @@ SELECT
SUM(shared_blks_dirtied) >= (SELECT rel_size FROM sizes) AS dirtied_ok
FROM pg_stat_statements;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index 0c20b8ce69b..f039110601a 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -59,7 +59,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -91,10 +91,10 @@ DROP FUNCTION PLUS_ONE(INTEGER);
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 04598e5ae46..83d1894a092 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Two-phase transactions
BEGIN;
@@ -123,7 +123,7 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Savepoints
BEGIN;
@@ -140,7 +140,7 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -185,7 +185,7 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -198,7 +198,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -210,7 +210,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -227,7 +227,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -240,7 +240,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -252,7 +252,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -260,7 +260,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Execution statements
SELECT 1 as a;
@@ -273,7 +273,7 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -299,7 +299,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -328,7 +328,7 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -341,4 +341,4 @@ SET enable_seqscan = on;
RESET enable_seqscan;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.39.2
v24-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v24-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 274e16cce2365fbe3d43a86d1b7208663a8f3a25 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Wed, 25 Oct 2023 18:16:57 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 116 +++++++------
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 23 ++-
.../pg_stat_statements/pg_stat_statements.c | 118 ++++++++++---
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 4 +-
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
9 files changed, 510 insertions(+), 96 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index eba4a95d91a..aecd1d6a2a0 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5a5404bbd57..ec317b0d6be 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 15b7c7f2b02..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
index 5fe211184bf..0bb2c397711 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index baff87b49e1..cb1ed1dd4ec 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -162,9 +162,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
@@ -235,6 +235,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;
@@ -318,6 +320,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);
@@ -369,7 +372,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,
@@ -664,6 +667,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 */
@@ -1380,11 +1385,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;
@@ -1454,18 +1471,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();
}
@@ -1478,8 +1511,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.
@@ -1712,6 +1745,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));
@@ -1780,6 +1815,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);
}
@@ -1876,6 +1913,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 :
@@ -1994,6 +2033,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;
@@ -2557,11 +2598,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;
@@ -2569,6 +2629,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,
@@ -2578,6 +2639,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. */
@@ -2587,22 +2650,22 @@ 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)
{
@@ -2610,19 +2673,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);
}
}
@@ -2636,7 +2697,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;
@@ -2674,6 +2734,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 2435c0c576e..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index 6c7ca962f81..15c589432e5 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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 @@
Total time spent by the statement on emitting code, 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>
--
2.39.2
On 25/10/2023 20:00, Andrei Zubkov wrote:
Hi Andrei,
On Wed, 2023-10-25 at 13:59 +0700, Andrei Lepikhov wrote:
But minmax_stats_since and changes in the UI of the reset routine
look like syntactic sugar here.
I can't convince myself that it is really needed. Do you have some
set of cases that can enforce the changes proposed?Yes, it looks strange, but it is needed in some way.
The main purpose of this patch is to provide means for sampling
solutions for collecting statistics data in series of samples. The
first goal here - is to be sure that the statement was not evicted and
come back between samples (especially between rare samples). This is
the target of the stats_since field. The second goal - is the ability
of getting all statistic increments for the interval between samples.
All statistics provided by pg_stat_statements with except of min/max
values can be calculated for the interval since the last sample knowing
the values in the last and current samples. We need a way to get
min/max values too. This is achieved by min/max reset performed by the
sampling solution. The minmax_stats_since field is here for the same
purpose - the sampling solution is need to be sure that no one have
done a min/max reset between samples. And if such reset was performed
at least we know when it was performed.
It is the gist of my question. If needed, You can remove the record by
(userid, dbOid, queryId). As I understand, this extension is usually
used by an administrator. Who can reset these parameters except you and
the DBMS?
--
regards,
Andrei Lepikhov
Postgres Professional
On Thu, 2023-10-26 at 15:49 +0700, Andrei Lepikhov wrote:
It is the gist of my question. If needed, You can remove the record
by
(userid, dbOid, queryId). As I understand, this extension is usually
used by an administrator. Who can reset these parameters except you
and
the DBMS?
This extension is used by administrator but indirectly through some
kind of sampling solution providing information about statistics change
over time. The only kind of statistics unavailable to sampling
solutions without a periodic reset is a min/max statistics. This patch
provides a way for resetting those statistics without entry eviction.
Suppose the DBA will use several sampling solutions. Every such
solution can perform its own resets of min/max statistics. Other
sampling solutions need a way to detect such resets to avoid undetected
interference. Timestamping of min/max reset can be used for that
purpose.
--
regards, Andrei Zubkov
Postgres Professional
On 25.10.2023 18:35, Andrei Zubkov wrote:
Hi Alena,
On Wed, 2023-10-25 at 16:25 +0300, Alena Rybakina wrote:
Hi! Thank you for your work on the subject.
1. I didn't understand why we first try to find pgssEntry with a
false top_level value, and later find it with a true top_level value.In case of pg_stat_statements the top_level field is the bool field of
the pgssHashKey struct used as the key for pgss_hash hashtable. When we
are performing a reset only userid, dbid and queryid values are
provided. We need to reset both top-level and non-top level entries. We
have only one way to get them all from a hashtable - search for entries
having top_level=true and with top_level=false in their keys.
Thank you for explanation, I got it.
2. And honestly, I think you should change
"Remove the entry if it exists, starting with the non-top-level
entry." on
"Remove the entry or reset min/max time statistic information and
the timestamp if it exists, starting with the non-top-level entry."
And the same with the comment bellow:
"Also reset the top-level entry if it exists."
"Also remove the entry or reset min/max time statistic information
and the timestamp if it exists."There are four such places actually - every time when the
SINGLE_ENTRY_RESET macro is used. The logic of reset performed is
described a bit in this macro definition. It seems quite redundant to
repeat this description four times. But I've noted that "remove" terms
should be replaced by "reset".I've attached v24 with commits updated.
Yes, I agree with the changes.
--
Regards,
Alena Rybakina
Hi hackers,
Patch rebased to the current master
--
regards, Andrei Zubkov
Postgres Professional
Attachments:
v25-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v25-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From ff2ff96352a843d32a1213a1e953503fd1525b7b Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 00:27:24 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 28 +--
.../expected/level_tracking.out | 80 ++++----
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 ++++++------
.../pg_stat_statements/expected/utility.out | 178 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 6 +-
.../pg_stat_statements/sql/level_tracking.sql | 12 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 32 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 299 insertions(+), 294 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index ede47a71acc..f6ac8da5ca2 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,16 +136,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
@@ -166,9 +166,9 @@ FROM pg_stat_statements;
t | t | t | t
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index 0b94c71c9c1..dada95e0f03 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,21 +49,21 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
@@ -125,10 +125,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -166,11 +166,11 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- immutable SQL function --- can be executed at plan time
@@ -200,10 +200,10 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -246,13 +246,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- immutable SQL function --- can be executed at plan time
@@ -287,10 +287,10 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -310,9 +310,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index a9134212909..fa738b5c00d 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Two-phase transactions
@@ -205,19 +205,19 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
1 | 0 | COMMIT PREPARED $1
2 | 0 | PREPARE TRANSACTION $1
1 | 0 | ROLLBACK PREPARED $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Savepoints
@@ -235,20 +235,20 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | BEGIN
1 | 0 | COMMIT
3 | 0 | RELEASE $1
4 | 0 | ROLLBACK TO $1
4 | 0 | SAVEPOINT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -284,7 +284,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -321,10 +321,10 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -346,22 +346,22 @@ CALL in_out(2, 1, 2);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | CALL in_out($1, $2, $3)
1 | 0 | CALL overload($1)
1 | 0 | CALL overload($1)
2 | 0 | CALL sum_one($1)
2 | 0 | CALL sum_two($1,$2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -391,14 +391,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -424,13 +424,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -450,13 +450,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -478,13 +478,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -499,13 +499,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Execution statements
@@ -534,19 +534,19 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+---------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -572,14 +572,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -591,10 +591,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -652,15 +652,15 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index 3b5d2afb858..9986b0a22d3 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -77,7 +77,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
INSERT INTO pgss_extend_temp_tab (a, b) SELECT generate_series(1, 1000), 'something';
WITH sizes AS (
@@ -92,4 +92,4 @@ SELECT
SUM(shared_blks_dirtied) >= (SELECT rel_size FROM sizes) AS dirtied_ok
FROM pg_stat_statements;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index dcd0b043580..85eb9153d3e 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -69,7 +69,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -101,7 +101,7 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -142,10 +142,10 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 3fb8dde68e2..4f7afece1de 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Two-phase transactions
BEGIN;
@@ -123,7 +123,7 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Savepoints
BEGIN;
@@ -140,7 +140,7 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -185,7 +185,7 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -198,7 +198,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -210,7 +210,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -227,7 +227,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -240,7 +240,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -252,7 +252,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -260,7 +260,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Execution statements
SELECT 1 as a;
@@ -273,7 +273,7 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -299,7 +299,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -328,4 +328,4 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.39.2
v25-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v25-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From 996cadb44a0f6cf64353311c553778ce156e21c4 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 00:28:47 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 116 +++++++------
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 23 ++-
.../pg_stat_statements/pg_stat_statements.c | 118 ++++++++++---
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 4 +-
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
9 files changed, 510 insertions(+), 96 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index eba4a95d91a..aecd1d6a2a0 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5a5404bbd57..ec317b0d6be 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 15b7c7f2b02..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
index 5fe211184bf..0bb2c397711 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6c63acf9898..16451763c5d 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -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,22 @@ 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 +2709,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 +2733,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 +2770,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 2435c0c576e..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b66dc7fa30a..acfacf828ce 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
--
2.39.2
A little fix in "level_tracking" tests after merge.
--
regards, Andrei Zubkov
Postgres Professional
Attachments:
v26-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.patchtext/x-patch; charset=UTF-8; name*0=v26-0001-pg_stat_statements-tests-Add-NOT-NULL-checking-of-pg.pat; name*1=chDownload
From ed7531ba471061346922bbcb00d92738f6515a3f Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 11:27:20 +0300
Subject: [PATCH 1/2] pg_stat_statements tests: Add NOT NULL checking of
pg_stat_statements_reset
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. It is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 28 +--
.../expected/level_tracking.out | 112 +++++------
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 ++++++------
.../pg_stat_statements/expected/utility.out | 178 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 6 +-
.../pg_stat_statements/sql/level_tracking.sql | 16 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 32 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 317 insertions(+), 312 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index ede47a71acc..f6ac8da5ca2 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,16 +136,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
@@ -166,9 +166,9 @@ FROM pg_stat_statements;
t | t | t | t
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index 0b94c71c9c1..fe01dd79afc 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- DO block - top-level tracking without utility.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DELETE FROM stats_track_tab;
@@ -88,18 +88,18 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+-----------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
t | 1 | DELETE FROM stats_track_tab
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(2 rows)
-- DO block - all-level tracking without utility.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DELETE FROM stats_track_tab;
@@ -114,21 +114,21 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+-----------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -166,11 +166,11 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- immutable SQL function --- can be executed at plan time
@@ -195,15 +195,15 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
t | 2 | 2 | SELECT PLUS_THREE($1)
t | 2 | 2 | SELECT PLUS_TWO($1)
t | 1 | 3 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- t | 1 | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -246,13 +246,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- immutable SQL function --- can be executed at plan time
@@ -280,17 +280,17 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
t | 2 | 2 | SELECT PLUS_TWO($1)
t | 1 | 5 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
f | 2 | 2 | SELECT i + $2 LIMIT $3
- t | 1 | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
--
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -310,9 +310,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index a9134212909..fa738b5c00d 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Two-phase transactions
@@ -205,19 +205,19 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
1 | 0 | COMMIT PREPARED $1
2 | 0 | PREPARE TRANSACTION $1
1 | 0 | ROLLBACK PREPARED $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Savepoints
@@ -235,20 +235,20 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | BEGIN
1 | 0 | COMMIT
3 | 0 | RELEASE $1
4 | 0 | ROLLBACK TO $1
4 | 0 | SAVEPOINT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -284,7 +284,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -321,10 +321,10 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -346,22 +346,22 @@ CALL in_out(2, 1, 2);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | CALL in_out($1, $2, $3)
1 | 0 | CALL overload($1)
1 | 0 | CALL overload($1)
2 | 0 | CALL sum_one($1)
2 | 0 | CALL sum_two($1,$2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -391,14 +391,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -424,13 +424,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -450,13 +450,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -478,13 +478,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -499,13 +499,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Execution statements
@@ -534,19 +534,19 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+---------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -572,14 +572,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -591,10 +591,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -652,15 +652,15 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index 3b5d2afb858..9986b0a22d3 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -77,7 +77,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
INSERT INTO pgss_extend_temp_tab (a, b) SELECT generate_series(1, 1000), 'something';
WITH sizes AS (
@@ -92,4 +92,4 @@ SELECT
SUM(shared_blks_dirtied) >= (SELECT rel_size FROM sizes) AS dirtied_ok
FROM pg_stat_statements;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index dcd0b043580..aa37408d521 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- DO block - top-level tracking without utility.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DELETE FROM stats_track_tab;
DO $$
BEGIN
@@ -52,7 +52,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- DO block - all-level tracking without utility.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DELETE FROM stats_track_tab;
DO $$
BEGIN
@@ -69,7 +69,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -101,7 +101,7 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -142,10 +142,10 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 3fb8dde68e2..4f7afece1de 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Two-phase transactions
BEGIN;
@@ -123,7 +123,7 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Savepoints
BEGIN;
@@ -140,7 +140,7 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -185,7 +185,7 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -198,7 +198,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -210,7 +210,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -227,7 +227,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -240,7 +240,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -252,7 +252,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -260,7 +260,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Execution statements
SELECT 1 as a;
@@ -273,7 +273,7 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -299,7 +299,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -328,4 +328,4 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.39.2
v26-0002-pg_stat_statements-Track-statement-entry-timestamp.patchtext/x-patch; charset=UTF-8; name=v26-0002-pg_stat_statements-Track-statement-entry-timestamp.patchDownload
From c58b0faa0cc35b0f3c5221f4e3d275a354ffe8bf Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 11:35:25 +0300
Subject: [PATCH 2/2] pg_stat_statements: Track statement entry timestamp
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 interval 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 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. 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 a
result.
Author: Andrei Zubkov (zubkov)
Reviewed by: Julien Rouhaud (rjuju), Hayato Kuroda (ha-kun),
Yuki Seino (seinoyu), Chengxi Sun (martin-sun),
Anton Melnikov (antonmel), Darren Rush (darrenr),
Michael Paquier (michael-kun), Sergei Kornilov (melkij),
Alena Rybakina (a.rybakina), Andrei Lepikhov (lepikhov)
Discussion: https://www.postgresql.org/message-id/flat/72e80e7b160a6eb189df9ef6f068cce3765d37f8.camel%40moonset.ru
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 116 +++++++------
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 23 ++-
.../pg_stat_statements/pg_stat_statements.c | 118 ++++++++++---
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 4 +-
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
9 files changed, 510 insertions(+), 96 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index eba4a95d91a..aecd1d6a2a0 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5a5404bbd57..ec317b0d6be 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 15b7c7f2b02..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
index 5fe211184bf..0bb2c397711 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6c63acf9898..16451763c5d 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -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,22 @@ 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 +2709,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 +2733,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 +2770,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 2435c0c576e..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b66dc7fa30a..acfacf828ce 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
--
2.39.2
Hi!
On Fri, Nov 17, 2023 at 10:40 AM Andrei Zubkov <zubkov@moonset.ru> wrote:
A little fix in "level_tracking" tests after merge.
I've reviewed this patch. I think this is the feature of high demand.
New columns (stats_since and minmax_stats_since) to the
pg_stat_statements view, enhancing the granularity and precision of
performance monitoring. This addition allows database administrators
to have a clearer understanding of the time intervals for statistics
collection on each statement. Such detailed tracking is invaluable for
performance tuning and identifying bottlenecks in database operations.
I think the design was well-discussed in this thread. Implementation
also looks good to me. I've just slightly revised the commit
messages.
I'd going to push this patchset if no objections.
------
Regards,
Alexander Korotkov
Attachments:
0002-Track-statement-entry-timestamp-in-contrib-pg_st-v27.patchapplication/octet-stream; name=0002-Track-statement-entry-timestamp-in-contrib-pg_st-v27.patchDownload
From ac51b84c6e262e7433bca36b2d40319bdad30aa2 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 11:35:25 +0300
Subject: [PATCH 2/2] 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 (lepikhov)
---
contrib/pg_stat_statements/Makefile | 2 +-
.../expected/entry_timestamp.out | 159 ++++++++++++++++++
.../expected/oldextversions.out | 116 +++++++------
contrib/pg_stat_statements/meson.build | 1 +
.../pg_stat_statements--1.10--1.11.sql | 23 ++-
.../pg_stat_statements/pg_stat_statements.c | 118 ++++++++++---
.../sql/entry_timestamp.sql | 114 +++++++++++++
.../pg_stat_statements/sql/oldextversions.sql | 4 +-
doc/src/sgml/pgstatstatements.sgml | 69 +++++++-
9 files changed, 510 insertions(+), 96 deletions(-)
create mode 100644 contrib/pg_stat_statements/expected/entry_timestamp.out
create mode 100644 contrib/pg_stat_statements/sql/entry_timestamp.sql
diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile
index eba4a95d91a..aecd1d6a2a0 100644
--- a/contrib/pg_stat_statements/Makefile
+++ b/contrib/pg_stat_statements/Makefile
@@ -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
diff --git a/contrib/pg_stat_statements/expected/entry_timestamp.out b/contrib/pg_stat_statements/expected/entry_timestamp.out
new file mode 100644
index 00000000000..a10c4be6bac
--- /dev/null
+++ b/contrib/pg_stat_statements/expected/entry_timestamp.out
@@ -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)
+
diff --git a/contrib/pg_stat_statements/expected/oldextversions.out b/contrib/pg_stat_statements/expected/oldextversions.out
index 5a5404bbd57..ec317b0d6be 100644
--- a/contrib/pg_stat_statements/expected/oldextversions.out
+++ b/contrib/pg_stat_statements/expected/oldextversions.out
@@ -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;
diff --git a/contrib/pg_stat_statements/meson.build b/contrib/pg_stat_statements/meson.build
index 15b7c7f2b02..81fe1eb917d 100644
--- a/contrib/pg_stat_statements/meson.build
+++ b/contrib/pg_stat_statements/meson.build
@@ -49,6 +49,7 @@ tests += {
'planning',
'user_activity',
'wal',
+ 'entry_timestamp',
'cleanup',
'oldextversions',
],
diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
index 5fe211184bf..0bb2c397711 100644
--- a/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
+++ b/contrib/pg_stat_statements/pg_stat_statements--1.10--1.11.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 6c63acf9898..16451763c5d 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -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,22 @@ 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 +2709,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 +2733,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 +2770,8 @@ done:
release_lock:
LWLockRelease(pgss->lock);
+
+ return stats_reset;
}
/*
diff --git a/contrib/pg_stat_statements/sql/entry_timestamp.sql b/contrib/pg_stat_statements/sql/entry_timestamp.sql
new file mode 100644
index 00000000000..d6d3027ab4f
--- /dev/null
+++ b/contrib/pg_stat_statements/sql/entry_timestamp.sql
@@ -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;
diff --git a/contrib/pg_stat_statements/sql/oldextversions.sql b/contrib/pg_stat_statements/sql/oldextversions.sql
index 2435c0c576e..ec06caa5ddc 100644
--- a/contrib/pg_stat_statements/sql/oldextversions.sql
+++ b/contrib/pg_stat_statements/sql/oldextversions.sql
@@ -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;
diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml
index b66dc7fa30a..acfacf828ce 100644
--- a/doc/src/sgml/pgstatstatements.sgml
+++ b/doc/src/sgml/pgstatstatements.sgml
@@ -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>
--
2.39.3 (Apple Git-145)
0001-Add-NOT-NULL-checking-of-pg_stat_statements_rese-v27.patchapplication/octet-stream; name=0001-Add-NOT-NULL-checking-of-pg_stat_statements_rese-v27.patchDownload
From 9b351f2b939d4790906dc3399b74a12f4e627c35 Mon Sep 17 00:00:00 2001
From: Andrei Zubkov <zubkov@moonset.ru>
Date: Fri, 17 Nov 2023 11:27:20 +0300
Subject: [PATCH 1/2] Add NOT NULL checking of pg_stat_statements_reset() in
tests
This is preliminary patch. It adds NOT NULL checking for the result of
pg_stat_statements_reset() function. I t is needed for upcoming patch
"Track statement entry timestamp" that will change the result type of
this function to the timestamp of a reset performed.
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 (lepikhov)
---
.../pg_stat_statements/expected/cursors.out | 28 +--
contrib/pg_stat_statements/expected/dml.out | 28 +--
.../expected/level_tracking.out | 112 +++++------
.../pg_stat_statements/expected/planning.out | 18 +-
.../pg_stat_statements/expected/select.out | 44 ++---
.../expected/user_activity.out | 120 ++++++------
.../pg_stat_statements/expected/utility.out | 178 +++++++++---------
contrib/pg_stat_statements/expected/wal.out | 10 +-
contrib/pg_stat_statements/sql/cursors.sql | 6 +-
contrib/pg_stat_statements/sql/dml.sql | 6 +-
.../pg_stat_statements/sql/level_tracking.sql | 16 +-
contrib/pg_stat_statements/sql/planning.sql | 4 +-
contrib/pg_stat_statements/sql/select.sql | 10 +-
.../pg_stat_statements/sql/user_activity.sql | 15 +-
contrib/pg_stat_statements/sql/utility.sql | 32 ++--
contrib/pg_stat_statements/sql/wal.sql | 2 +-
16 files changed, 317 insertions(+), 312 deletions(-)
diff --git a/contrib/pg_stat_statements/expected/cursors.out b/contrib/pg_stat_statements/expected/cursors.out
index 46375ea9051..0fc4b2c098d 100644
--- a/contrib/pg_stat_statements/expected/cursors.out
+++ b/contrib/pg_stat_statements/expected/cursors.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DECLARE
@@ -20,13 +20,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------------------------------------------------------
2 | 0 | CLOSE cursor_stats_1
2 | 0 | DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- FETCH
@@ -59,12 +59,12 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DECLARE cursor_stats_2 CURSOR WITH HOLD FOR SELECT $1
1 | 1 | FETCH 1 IN cursor_stats_1
1 | 1 | FETCH 1 IN cursor_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(9 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/dml.out b/contrib/pg_stat_statements/expected/dml.out
index ede47a71acc..f6ac8da5ca2 100644
--- a/contrib/pg_stat_statements/expected/dml.out
+++ b/contrib/pg_stat_statements/expected/dml.out
@@ -81,16 +81,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 12 | SELECT * FROM pgss_dml_tab ORDER BY a
2 | 4 | SELECT * FROM pgss_dml_tab WHERE a > $1 ORDER BY a
1 | 8 | SELECT * FROM pgss_dml_tab WHERE a IN ($1, $2, $3, $4, $5)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET pg_stat_statements.track_utility = FALSE
6 | 6 | UPDATE pgss_dml_tab SET b = $1 WHERE a = $2
1 | 3 | UPDATE pgss_dml_tab SET b = $1 WHERE a > $2
(10 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- MERGE
@@ -136,16 +136,16 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | WHEN NOT MATCHED THEN INSERT (a, b) VALUES ($1, $2)
1 | 0 | MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a) +
| | WHEN NOT MATCHED THEN INSERT (b, a) VALUES ($1, $2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(10 rows)
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
@@ -166,9 +166,9 @@ FROM pg_stat_statements;
t | t | t | t
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/level_tracking.out b/contrib/pg_stat_statements/expected/level_tracking.out
index 0b94c71c9c1..fe01dd79afc 100644
--- a/contrib/pg_stat_statements/expected/level_tracking.out
+++ b/contrib/pg_stat_statements/expected/level_tracking.out
@@ -2,10 +2,10 @@
-- Statement level tracking
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - top-level tracking.
@@ -29,10 +29,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
| | $$ LANGUAGE plpgsql
(2 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- DO block - all-level tracking.
@@ -49,31 +49,31 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+--------------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
- t | 1 | DO $$ +
- | | BEGIN +
- | | DELETE FROM stats_track_tab; +
+ t | 1 | DO $$ +
+ | | BEGIN +
+ | | DELETE FROM stats_track_tab; +
| | END; $$
- t | 1 | DO LANGUAGE plpgsql $$ +
- | | BEGIN +
- | | -- this is a SELECT +
- | | PERFORM 'hello world'::TEXT; +
+ t | 1 | DO LANGUAGE plpgsql $$ +
+ | | BEGIN +
+ | | -- this is a SELECT +
+ | | PERFORM 'hello world'::TEXT; +
| | END; $$
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
t | 1 | SET pg_stat_statements.track = 'all'
(7 rows)
-- DO block - top-level tracking without utility.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DELETE FROM stats_track_tab;
@@ -88,18 +88,18 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+-----------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
t | 1 | DELETE FROM stats_track_tab
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(2 rows)
-- DO block - all-level tracking without utility.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
DELETE FROM stats_track_tab;
@@ -114,21 +114,21 @@ BEGIN
END; $$;
SELECT toplevel, calls, query FROM pg_stat_statements
ORDER BY query COLLATE "C", toplevel;
- toplevel | calls | query
-----------+-------+-----------------------------------
+ toplevel | calls | query
+----------+-------+----------------------------------------------------
f | 1 | DELETE FROM stats_track_tab
t | 1 | DELETE FROM stats_track_tab
f | 1 | SELECT $1::TEXT
- t | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
@@ -166,11 +166,11 @@ SELECT PLUS_ONE(10);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- immutable SQL function --- can be executed at plan time
@@ -195,15 +195,15 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
t | 2 | 2 | SELECT PLUS_THREE($1)
t | 2 | 2 | SELECT PLUS_TWO($1)
t | 1 | 3 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- t | 1 | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- we drop and recreate the functions to avoid any caching funnies
@@ -246,13 +246,13 @@ SELECT PLUS_ONE(1);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 2 | SELECT (i + $2 + $3)::INTEGER
2 | 2 | SELECT (i + $2)::INTEGER LIMIT $3
2 | 2 | SELECT PLUS_ONE($1)
2 | 2 | SELECT PLUS_TWO($1)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-- immutable SQL function --- can be executed at plan time
@@ -280,17 +280,17 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
t | 2 | 2 | SELECT PLUS_TWO($1)
t | 1 | 5 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
f | 2 | 2 | SELECT i + $2 LIMIT $3
- t | 1 | 1 | SELECT pg_stat_statements_reset()
+ t | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
--
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT 1 AS "one";
@@ -310,9 +310,9 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+-------
(0 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/planning.out b/contrib/pg_stat_statements/expected/planning.out
index c3561dd7da3..9effd11fdc8 100644
--- a/contrib/pg_stat_statements/expected/planning.out
+++ b/contrib/pg_stat_statements/expected/planning.out
@@ -3,10 +3,10 @@
--
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -64,7 +64,7 @@ SELECT plans, calls, rows, query FROM pg_stat_statements
0 | 1 | 0 | ALTER TABLE stats_plan_test ADD COLUMN x int
0 | 1 | 0 | CREATE TABLE stats_plan_test ()
3 | 3 | 3 | SELECT $1
- 0 | 1 | 1 | SELECT pg_stat_statements_reset()
+ 0 | 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | 0 | SELECT plans, calls, rows, query FROM pg_stat_statements+
| | | WHERE query NOT LIKE $1 ORDER BY query COLLATE "C"
(5 rows)
@@ -80,9 +80,9 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/select.out b/contrib/pg_stat_statements/expected/select.out
index 972539b2c51..dd6c756f67d 100644
--- a/contrib/pg_stat_statements/expected/select.out
+++ b/contrib/pg_stat_statements/expected/select.out
@@ -4,10 +4,10 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -138,7 +138,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 2 | SELECT $1 AS i UNION SELECT $2 ORDER BY i
1 | 1 | SELECT $1 || $2
0 | 0 | SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 2 | WITH t(f) AS ( +
| | VALUES ($1), ($2) +
| | ) +
@@ -146,10 +146,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | select $1::jsonb ? $2
(12 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -157,10 +157,10 @@ SELECT pg_stat_statements_reset();
--
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- control query
@@ -236,17 +236,17 @@ SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE OF pgss_b, pgss_a
1 | SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id FOR UPDATE SKIP LOCKED
0 | SELECT calls, query FROM pg_stat_statements ORDER BY query COLLATE "C"
- 1 | SELECT pg_stat_statements_reset()
+ 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT dealloc FROM pg_stat_statements_info;
@@ -406,9 +406,9 @@ SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
2
(1 row)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/user_activity.out b/contrib/pg_stat_statements/expected/user_activity.out
index f3c6b6ab326..38faf18c7c5 100644
--- a/contrib/pg_stat_statements/expected/user_activity.out
+++ b/contrib/pg_stat_statements/expected/user_activity.out
@@ -2,10 +2,10 @@
-- Track user activity and reset them
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CREATE ROLE regress_stats_user1;
@@ -39,27 +39,27 @@ SELECT 1+1 AS "TWO";
RESET ROLE;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
------------------------------------+-------+------
- CREATE ROLE regress_stats_user1 | 1 | 0
- CREATE ROLE regress_stats_user2 | 1 | 0
- RESET ROLE | 2 | 0
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1 AS "ONE" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
- SET ROLE regress_stats_user1 | 1 | 0
- SET ROLE regress_stats_user2 | 1 | 0
+ query | calls | rows
+----------------------------------------------------+-------+------
+ CREATE ROLE regress_stats_user1 | 1 | 0
+ CREATE ROLE regress_stats_user2 | 1 | 0
+ RESET ROLE | 2 | 0
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1 AS "ONE" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT $1+$2 AS "TWO" | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SET ROLE regress_stats_user1 | 1 | 0
+ SET ROLE regress_stats_user2 | 1 | 0
(10 rows)
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
+ t
+---
+ f
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -72,8 +72,8 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
SELECT $1 AS "ONE" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
SELECT $1+$2 AS "TWO" | 1 | 1
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 1 | 10
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -87,10 +87,11 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
- pg_stat_statements_reset
---------------------------
-
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -106,9 +107,10 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset() | 1 | 1
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 2 | 22
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -117,12 +119,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
- pg_stat_statements_reset
---------------------------
-
-
+ t
+---
+ t
+ t
(2 rows)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -136,11 +138,12 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 3 | 34
SET ROLE regress_stats_user1 | 1 | 0
SET ROLE regress_stats_user2 | 1 | 0
@@ -149,11 +152,11 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
- pg_stat_statements_reset
---------------------------
-
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
@@ -166,12 +169,13 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = $1), +| |
(SELECT d.oid FROM pg_database As d where datname = current_database()),+| |
(SELECT s.queryid FROM pg_stat_statements AS s +| |
- WHERE s.query = $2 LIMIT $3)) | |
- SELECT pg_stat_statements_reset($1) | 1 | 1
- SELECT pg_stat_statements_reset($1,$2,s.queryid) +| 1 | 2
+ WHERE s.query = $2 LIMIT $3)) +| |
+ IS NOT NULL AS t | |
+ SELECT pg_stat_statements_reset($1) IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset($1,$2,s.queryid) IS NOT NULL AS t +| 1 | 2
FROM pg_stat_statements AS s WHERE s.query = $3 | |
- SELECT pg_stat_statements_reset() | 1 | 1
- SELECT pg_stat_statements_reset(r.oid) +| 1 | 1
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1
+ SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t +| 1 | 1
FROM pg_roles AS r WHERE r.rolname = $1 | |
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C" | 4 | 45
SET ROLE regress_stats_user2 | 1 | 0
@@ -180,16 +184,16 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
- query | calls | rows
-----------------------------------------+-------+------
- SELECT pg_stat_statements_reset(0,0,0) | 1 | 1
+ query | calls | rows
+---------------------------------------------------------+-------+------
+ SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t | 1 | 1
(1 row)
--
@@ -197,9 +201,9 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/utility.out b/contrib/pg_stat_statements/expected/utility.out
index a9134212909..fa738b5c00d 100644
--- a/contrib/pg_stat_statements/expected/utility.out
+++ b/contrib/pg_stat_statements/expected/utility.out
@@ -3,10 +3,10 @@
--
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Tables, indexes, triggers
@@ -33,13 +33,13 @@ NOTICE: table "tab_stats" does not exist, skipping
1 | 0 | CREATE TEMP TABLE tab_stats (a int, b char(20))
3 | 0 | DROP TABLE IF EXISTS tab_stats
1 | 0 | DROP TABLE tab_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Partitions
@@ -142,13 +142,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | DROP TABLE trigger_tab_stats
1 | 0 | DROP TYPE stats_type
1 | 0 | DROP VIEW view_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(39 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Transaction statements
@@ -188,13 +188,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE
1 | 0 | BEGIN TRANSACTION READ ONLY, READ WRITE, DEFERRABLE, NOT DEFERRABLE
7 | 0 | COMMIT WORK
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Two-phase transactions
@@ -205,19 +205,19 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
1 | 0 | COMMIT PREPARED $1
2 | 0 | PREPARE TRANSACTION $1
1 | 0 | ROLLBACK PREPARED $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Savepoints
@@ -235,20 +235,20 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
1 | 0 | BEGIN
1 | 0 | COMMIT
3 | 0 | RELEASE $1
4 | 0 | ROLLBACK TO $1
4 | 0 | SAVEPOINT $1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- EXPLAIN statements
@@ -284,7 +284,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-------+------+---------------------------------------------------------------------------------
2 | 0 | EXPLAIN (costs off) SELECT $1
2 | 0 | EXPLAIN (costs off) SELECT a FROM generate_series($1,$2) AS tab(a) WHERE a = $3
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-- CALL
@@ -321,10 +321,10 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
CALL sum_one(3);
@@ -346,22 +346,22 @@ CALL in_out(2, 1, 2);
(1 row)
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-----------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | CALL in_out($1, $2, $3)
1 | 0 | CALL overload($1)
1 | 0 | CALL overload($1)
2 | 0 | CALL sum_one($1)
2 | 0 | CALL sum_two($1,$2)
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(6 rows)
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Some queries with A_Const nodes.
@@ -391,14 +391,14 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | COPY (SELECT 2) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 1 RETURNING *) TO STDOUT
1 | 2 | COPY (UPDATE copy_stats SET b = b + 2 RETURNING *) TO STDOUT
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(8 rows)
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE TABLE AS
@@ -424,13 +424,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP TABLE ctas_stats_1
2 | 0 | DROP TABLE ctas_stats_2
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE MATERIALIZED VIEW
@@ -450,13 +450,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, $1::int AS col2 +
| | FROM generate_series($2, $3) AS tab(a) WHERE a < $4 AND a > $5
2 | 0 | DROP MATERIALIZED VIEW matview_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(3 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- CREATE VIEW
@@ -478,13 +478,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
| | SELECT a AS col1, 4::int AS col2 +
| | FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3
2 | 0 | DROP VIEW view_stats_1
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(4 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Domains
@@ -499,13 +499,13 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | ALTER DOMAIN domain_stats SET DEFAULT '3'
1 | 0 | CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0)
1 | 0 | DROP DOMAIN domain_stats
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- Execution statements
@@ -534,19 +534,19 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+---------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | DEALLOCATE $1
2 | 0 | DEALLOCATE ALL
2 | 2 | PREPARE stat_select AS SELECT $1 AS a
1 | 1 | SELECT $1 as a
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
-- SET statements.
@@ -572,14 +572,14 @@ SET LOCAL SESSION AUTHORIZATION DEFAULT;
RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
- calls | rows | query
--------+------+-------------------------------------------------
+ calls | rows | query
+-------+------+----------------------------------------------------
2 | 0 | BEGIN
2 | 0 | COMMIT
2 | 0 | RESET SESSION AUTHORIZATION
1 | 0 | RESET enable_seqscan
1 | 0 | RESET work_mem
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1 | 0 | SET LOCAL SESSION AUTHORIZATION DEFAULT
1 | 0 | SET SESSION SESSION AUTHORIZATION DEFAULT
1 | 0 | SET TRANSACTION ISOLATION LEVEL READ COMMITTED
@@ -591,10 +591,10 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 0 | SET work_mem = '2MB'
(15 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
--
@@ -652,15 +652,15 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
1 | 1 | FETCH NEXT pgss_cursor
1 | 13 | REFRESH MATERIALIZED VIEW pgss_matv
1 | 10 | SELECT generate_series($1, $2) c INTO pgss_select_into
- 1 | 1 | SELECT pg_stat_statements_reset()
+ 1 | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
(12 rows)
DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/expected/wal.out b/contrib/pg_stat_statements/expected/wal.out
index 9896ba25363..34a2bf5b033 100644
--- a/contrib/pg_stat_statements/expected/wal.out
+++ b/contrib/pg_stat_statements/expected/wal.out
@@ -17,14 +17,14 @@ FROM pg_stat_statements ORDER BY query COLLATE "C";
--------------------------------------------------------------+-------+------+---------------------+-----------------------+---------------------
DELETE FROM pgss_wal_tab WHERE a > $1 | 1 | 1 | t | t | t
INSERT INTO pgss_wal_tab VALUES(generate_series($1, $2), $3) | 1 | 10 | t | t | t
- SELECT pg_stat_statements_reset() | 1 | 1 | f | f | f
+ SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1 | 1 | f | f | f
SET pg_stat_statements.track_utility = FALSE | 1 | 0 | f | f | t
UPDATE pgss_wal_tab SET b = $1 WHERE a > $2 | 1 | 3 | t | t | t
(5 rows)
-SELECT pg_stat_statements_reset();
- pg_stat_statements_reset
---------------------------
-
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
+ t
+---
+ t
(1 row)
diff --git a/contrib/pg_stat_statements/sql/cursors.sql b/contrib/pg_stat_statements/sql/cursors.sql
index cef6dc9e1b8..61738ac470e 100644
--- a/contrib/pg_stat_statements/sql/cursors.sql
+++ b/contrib/pg_stat_statements/sql/cursors.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DECLARE
-- SELECT is normalized.
@@ -14,7 +14,7 @@ DECLARE cursor_stats_1 CURSOR WITH HOLD FOR SELECT 2;
CLOSE cursor_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- FETCH
BEGIN;
@@ -27,4 +27,4 @@ CLOSE cursor_stats_2;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/dml.sql b/contrib/pg_stat_statements/sql/dml.sql
index 3b5d2afb858..9986b0a22d3 100644
--- a/contrib/pg_stat_statements/sql/dml.sql
+++ b/contrib/pg_stat_statements/sql/dml.sql
@@ -46,7 +46,7 @@ SELECT * FROM pgss_dml_tab ORDER BY a;
SELECT * FROM pgss_dml_tab WHERE a IN (1, 2, 3, 4, 5);
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- MERGE
MERGE INTO pgss_dml_tab USING pgss_dml_tab st ON (st.a = pgss_dml_tab.a AND st.a >= 4)
@@ -77,7 +77,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- check that [temp] table relation extensions are tracked as writes
CREATE TABLE pgss_extend_tab (a int, b text);
CREATE TEMP TABLE pgss_extend_temp_tab (a int, b text);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
INSERT INTO pgss_extend_tab (a, b) SELECT generate_series(1, 1000), 'something';
INSERT INTO pgss_extend_temp_tab (a, b) SELECT generate_series(1, 1000), 'something';
WITH sizes AS (
@@ -92,4 +92,4 @@ SELECT
SUM(shared_blks_dirtied) >= (SELECT rel_size FROM sizes) AS dirtied_ok
FROM pg_stat_statements;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/level_tracking.sql b/contrib/pg_stat_statements/sql/level_tracking.sql
index dcd0b043580..aa37408d521 100644
--- a/contrib/pg_stat_statements/sql/level_tracking.sql
+++ b/contrib/pg_stat_statements/sql/level_tracking.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - top-level tracking.
CREATE TABLE stats_track_tab (x int);
@@ -16,7 +16,7 @@ END;
$$ LANGUAGE plpgsql;
SELECT toplevel, calls, query FROM pg_stat_statements
WHERE query LIKE '%DELETE%' ORDER BY query COLLATE "C", toplevel;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- DO block - all-level tracking.
SET pg_stat_statements.track = 'all';
@@ -36,7 +36,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- DO block - top-level tracking without utility.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DELETE FROM stats_track_tab;
DO $$
BEGIN
@@ -52,7 +52,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- DO block - all-level tracking without utility.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
DELETE FROM stats_track_tab;
DO $$
BEGIN
@@ -69,7 +69,7 @@ SELECT toplevel, calls, query FROM pg_stat_statements
-- PL/pgSQL function - top-level tracking.
SET pg_stat_statements.track = 'top';
SET pg_stat_statements.track_utility = FALSE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
DECLARE
r INTEGER;
@@ -101,7 +101,7 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- PL/pgSQL function - all-level tracking.
SET pg_stat_statements.track = 'all';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- we drop and recreate the functions to avoid any caching funnies
DROP FUNCTION PLUS_ONE(INTEGER);
@@ -142,10 +142,10 @@ SELECT toplevel, calls, rows, query FROM pg_stat_statements ORDER BY query COLLA
-- pg_stat_statements.track = none
--
SET pg_stat_statements.track = 'none';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT 1 AS "one";
SELECT 1 + 1 AS "two";
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/planning.sql b/contrib/pg_stat_statements/sql/planning.sql
index a59b9363c4b..46f5d9b951c 100644
--- a/contrib/pg_stat_statements/sql/planning.sql
+++ b/contrib/pg_stat_statements/sql/planning.sql
@@ -4,7 +4,7 @@
-- These tests require track_planning to be enabled.
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- [re]plan counting
@@ -28,4 +28,4 @@ SELECT plans >= 2 AND plans <= calls AS plans_ok, calls, rows, query FROM pg_sta
-- Cleanup
DROP TABLE stats_plan_test;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/select.sql b/contrib/pg_stat_statements/sql/select.sql
index eef7b0bbf58..eb45cb81ad2 100644
--- a/contrib/pg_stat_statements/sql/select.sql
+++ b/contrib/pg_stat_statements/sql/select.sql
@@ -5,7 +5,7 @@
CREATE EXTENSION pg_stat_statements;
SET pg_stat_statements.track_utility = FALSE;
SET pg_stat_statements.track_planning = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- simple and compound statements
@@ -56,7 +56,7 @@ EXECUTE pgss_test(1);
DEALLOCATE pgss_test;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- queries with locking clauses
@@ -64,7 +64,7 @@ SELECT pg_stat_statements_reset();
CREATE TABLE pgss_a (id integer PRIMARY KEY);
CREATE TABLE pgss_b (id integer PRIMARY KEY, a_id integer REFERENCES pgss_a);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- control query
SELECT * FROM pgss_a JOIN pgss_b ON pgss_b.a_id = pgss_a.id;
@@ -92,7 +92,7 @@ DROP TABLE pgss_a, pgss_b CASCADE;
--
-- access to pg_stat_statements_info view
--
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
SELECT dealloc FROM pg_stat_statements_info;
-- FROM [ONLY]
@@ -146,4 +146,4 @@ SELECT (
) FROM (VALUES(6,7)) v3(e,f) GROUP BY ROLLUP(e,f);
SELECT COUNT(*) FROM pg_stat_statements WHERE query LIKE '%SELECT GROUPING%';
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/user_activity.sql b/contrib/pg_stat_statements/sql/user_activity.sql
index 4b95edda890..07a5f36fc12 100644
--- a/contrib/pg_stat_statements/sql/user_activity.sql
+++ b/contrib/pg_stat_statements/sql/user_activity.sql
@@ -3,7 +3,7 @@
--
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CREATE ROLE regress_stats_user1;
CREATE ROLE regress_stats_user2;
@@ -24,7 +24,7 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- Don't reset anything if any of the parameter is NULL
--
-SELECT pg_stat_statements_reset(NULL);
+SELECT pg_stat_statements_reset(NULL) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -35,27 +35,28 @@ SELECT pg_stat_statements_reset(
(SELECT r.oid FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user2'),
(SELECT d.oid FROM pg_database As d where datname = current_database()),
(SELECT s.queryid FROM pg_stat_statements AS s
- WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1));
+ WHERE s.query = 'SELECT $1+$2 AS "TWO"' LIMIT 1))
+ IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query ('SELECT $1 AS "ONE"') executed by two users
--
-SELECT pg_stat_statements_reset(0,0,s.queryid)
+SELECT pg_stat_statements_reset(0,0,s.queryid) IS NOT NULL AS t
FROM pg_stat_statements AS s WHERE s.query = 'SELECT $1 AS "ONE"';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- remove query of a user (regress_stats_user1)
--
-SELECT pg_stat_statements_reset(r.oid)
+SELECT pg_stat_statements_reset(r.oid) IS NOT NULL AS t
FROM pg_roles AS r WHERE r.rolname = 'regress_stats_user1';
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
-- reset all
--
-SELECT pg_stat_statements_reset(0,0,0);
+SELECT pg_stat_statements_reset(0,0,0) IS NOT NULL AS t;
SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
@@ -63,4 +64,4 @@ SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C";
--
DROP ROLE regress_stats_user1;
DROP ROLE regress_stats_user2;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/utility.sql b/contrib/pg_stat_statements/sql/utility.sql
index 3fb8dde68e2..4f7afece1de 100644
--- a/contrib/pg_stat_statements/sql/utility.sql
+++ b/contrib/pg_stat_statements/sql/utility.sql
@@ -4,7 +4,7 @@
-- These tests require track_utility to be enabled.
SET pg_stat_statements.track_utility = TRUE;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Tables, indexes, triggers
CREATE TEMP TABLE tab_stats (a int, b char(20));
@@ -18,7 +18,7 @@ DROP TABLE IF EXISTS tab_stats \;
DROP TABLE IF EXISTS tab_stats \;
Drop Table If Exists tab_stats \;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Partitions
CREATE TABLE pt_stats (a int, b int) PARTITION BY range (a);
@@ -83,7 +83,7 @@ CREATE STATISTICS tab_expr_stats_1 (mcv) ON a, (2*a), (3*b) FROM tab_expr_stats;
DROP TABLE tab_expr_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Transaction statements
BEGIN;
@@ -113,7 +113,7 @@ COMMIT;
BEGIN TRANSACTION NOT DEFERRABLE, READ ONLY, READ WRITE, DEFERRABLE;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Two-phase transactions
BEGIN;
@@ -123,7 +123,7 @@ BEGIN;
PREPARE TRANSACTION 'stat_trans2';
ROLLBACK PREPARED 'stat_trans2';
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Savepoints
BEGIN;
@@ -140,7 +140,7 @@ ROLLBACK TO sp1;
RELEASE SAVEPOINT sp1;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- EXPLAIN statements
-- A Query is used, normalized by the query jumbling.
@@ -185,7 +185,7 @@ BEGIN
i2 := i;
i3 := i3 + i;
END; $$ LANGUAGE plpgsql;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
CALL sum_one(3);
CALL sum_one(199);
CALL sum_two(1,1);
@@ -198,7 +198,7 @@ SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-- COPY
CREATE TABLE copy_stats (a int, b int);
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Some queries with A_Const nodes.
COPY (SELECT 1) TO STDOUT;
COPY (SELECT 2) TO STDOUT;
@@ -210,7 +210,7 @@ COPY (DELETE FROM copy_stats WHERE a = 1 RETURNING *) TO STDOUT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
DROP TABLE copy_stats;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE TABLE AS
-- SELECT queries are normalized, creating matching query IDs.
@@ -227,7 +227,7 @@ CREATE TABLE ctas_stats_2 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 1;
DROP TABLE ctas_stats_2;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE MATERIALIZED VIEW
-- SELECT queries are normalized, creating matching query IDs.
@@ -240,7 +240,7 @@ CREATE MATERIALIZED VIEW matview_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP MATERIALIZED VIEW matview_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- CREATE VIEW
CREATE VIEW view_stats_1 AS
@@ -252,7 +252,7 @@ CREATE VIEW view_stats_1 AS
FROM generate_series(1, 5) AS tab(a) WHERE a < 4 AND a > 3;
DROP VIEW view_stats_1;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Domains
CREATE DOMAIN domain_stats AS int CHECK (VALUE > 0);
@@ -260,7 +260,7 @@ ALTER DOMAIN domain_stats SET DEFAULT '3';
ALTER DOMAIN domain_stats ADD CONSTRAINT higher_than_one CHECK (VALUE > 1);
DROP DOMAIN domain_stats;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- Execution statements
SELECT 1 as a;
@@ -273,7 +273,7 @@ DEALLOCATE PREPARE stat_select;
DEALLOCATE ALL;
DEALLOCATE PREPARE ALL;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
-- SET statements.
-- These use two different strings, still they count as one entry.
@@ -299,7 +299,7 @@ RESET SESSION AUTHORIZATION;
COMMIT;
SELECT calls, rows, query FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
-- Track the total number of rows retrieved or affected by the utility
@@ -328,4 +328,4 @@ DROP MATERIALIZED VIEW pgss_matv;
DROP TABLE pgss_ctas;
DROP TABLE pgss_select_into;
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
diff --git a/contrib/pg_stat_statements/sql/wal.sql b/contrib/pg_stat_statements/sql/wal.sql
index 34b21c0fa98..1dc1552a81e 100644
--- a/contrib/pg_stat_statements/sql/wal.sql
+++ b/contrib/pg_stat_statements/sql/wal.sql
@@ -17,4 +17,4 @@ wal_bytes > 0 as wal_bytes_generated,
wal_records > 0 as wal_records_generated,
wal_records >= rows as wal_records_ge_rows
FROM pg_stat_statements ORDER BY query COLLATE "C";
-SELECT pg_stat_statements_reset();
+SELECT pg_stat_statements_reset() IS NOT NULL AS t;
--
2.39.3 (Apple Git-145)
Hi,
On Sat, Nov 25, 2023 at 02:45:07AM +0200, Alexander Korotkov wrote:
I've reviewed this patch. I think this is the feature of high demand.
New columns (stats_since and minmax_stats_since) to the
pg_stat_statements view, enhancing the granularity and precision of
performance monitoring. This addition allows database administrators
to have a clearer understanding of the time intervals for statistics
collection on each statement. Such detailed tracking is invaluable for
performance tuning and identifying bottlenecks in database operations.
Yes, it will greatly improve performance analysis tools, and as the maintainer
of one of them I've been waiting for this feature for a very long time!
I think the design was well-discussed in this thread. Implementation
also looks good to me. I've just slightly revised the commit
messages.I'd going to push this patchset if no objections.
Thanks! No objection from me, it all looks good.
Hi Alexander!
On Sat, 2023-11-25 at 02:45 +0200, Alexander Korotkov wrote:
I've reviewed this patch.
Thank you very much for your review.
I think the design was well-discussed in this thread. Implementation
also looks good to me. I've just slightly revised the commit
messages.
I've noted a strange space in a commit message of 0001 patch:
"I t is needed for upcoming patch..."
It looks like a typo.
--
regards, Andrei Zubkov
Postgres Professional
On Sat, Nov 25, 2023 at 10:45 PM Andrei Zubkov <zubkov@moonset.ru> wrote:
On Sat, 2023-11-25 at 02:45 +0200, Alexander Korotkov wrote:
I've reviewed this patch.
Thank you very much for your review.
I think the design was well-discussed in this thread. Implementation
also looks good to me. I've just slightly revised the commit
messages.I've noted a strange space in a commit message of 0001 patch:
"I t is needed for upcoming patch..."
It looks like a typo.
Thank you for catching it. I'll fix this before commit.
------
Regards,
Alexander Korotkov
Hello Alexander,
25.11.2023 23:46, Alexander Korotkov wrote:
On Sat, Nov 25, 2023 at 10:45 PM Andrei Zubkov<zubkov@moonset.ru> wrote:
I've noted a strange space in a commit message of 0001 patch:
"I t is needed for upcoming patch..."
It looks like a typo.Thank you for catching it. I'll fix this before commit.
I've found one more typo in that commit: minimun.
Best regards,
Alexander