From: Michael Paquier Date: Wed, 8 Oct 2025 03:43:40 +0000 (+0900) Subject: Add stats_reset to pg_stat_user_functions X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b71bae41a0cdda879db39d9946d2cc4af910bed1;p=thirdparty%2Fpostgresql.git Add stats_reset to pg_stat_user_functions It is possible to call pg_stat_reset_single_function_counters() for a single function, but the reset time was missing the system view showing its statistics. Like all the fields of pg_stat_user_functions, the GUC track_functions needs to be enabled to show the statistics about function executions. Bump catalog version. Bump PGSTAT_FILE_FORMAT_ID, as a result of the new field added to PgStat_StatFuncEntry. Author: Bertrand Drouvot Discussion: https://postgr.es/m/aONjnsaJSx-nEdfU@paquier.xyz --- diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 6e3aac3d815..789ac16b444 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -4736,6 +4736,15 @@ description | Waiting for a newly initialized WAL file to reach durable storage other functions called by it, in milliseconds + + + + stats_reset timestamp with time zone + + + Time at which these statistics were last reset + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 884b6a23817..c94f1f05f52 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -1131,7 +1131,8 @@ CREATE VIEW pg_stat_user_functions AS P.proname AS funcname, pg_stat_get_function_calls(P.oid) AS calls, pg_stat_get_function_total_time(P.oid) AS total_time, - pg_stat_get_function_self_time(P.oid) AS self_time + pg_stat_get_function_self_time(P.oid) AS self_time, + pg_stat_get_function_stat_reset_time(P.oid) AS stats_reset FROM pg_proc P LEFT JOIN pg_namespace N ON (N.oid = P.pronamespace) WHERE P.prolang != 12 -- fast check to eliminate built-in functions AND pg_stat_get_function_calls(P.oid) IS NOT NULL; diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index 48f57e408e1..7ef06150df7 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -328,6 +328,7 @@ static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] .pending_size = sizeof(PgStat_FunctionCounts), .flush_pending_cb = pgstat_function_flush_cb, + .reset_timestamp_cb = pgstat_function_reset_timestamp_cb, }, [PGSTAT_KIND_REPLSLOT] = { diff --git a/src/backend/utils/activity/pgstat_function.c b/src/backend/utils/activity/pgstat_function.c index 6214f93d36e..b5db9d15e07 100644 --- a/src/backend/utils/activity/pgstat_function.c +++ b/src/backend/utils/activity/pgstat_function.c @@ -214,6 +214,12 @@ pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) return true; } +void +pgstat_function_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts) +{ + ((PgStatShared_Function *) header)->stats.stat_reset_timestamp = ts; +} + /* * find any existing PgStat_FunctionCounts entry for specified function * diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 7e89a8048d5..3802a4cb888 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -203,6 +203,24 @@ PG_STAT_GET_FUNCENTRY_FLOAT8_MS(total_time) /* pg_stat_get_function_self_time */ PG_STAT_GET_FUNCENTRY_FLOAT8_MS(self_time) +Datum +pg_stat_get_function_stat_reset_time(PG_FUNCTION_ARGS) +{ + Oid funcid = PG_GETARG_OID(0); + TimestampTz result; + PgStat_StatFuncEntry *funcentry; + + if ((funcentry = pgstat_fetch_stat_funcentry(funcid)) == NULL) + result = 0; + else + result = funcentry->stat_reset_timestamp; + + if (result == 0) + PG_RETURN_NULL(); + else + PG_RETURN_TIMESTAMPTZ(result); +} + Datum pg_stat_get_backend_idset(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 642abe5217e..00b3bce1e12 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202510062 +#define CATALOG_VERSION_NO 202510081 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 7c20180637f..6bb31892d1d 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6071,6 +6071,10 @@ proname => 'pg_stat_get_function_self_time', provolatile => 's', proparallel => 'r', prorettype => 'float8', proargtypes => 'oid', prosrc => 'pg_stat_get_function_self_time' }, +{ oid => '8745', descr => 'statistics: last reset for a function', + proname => 'pg_stat_get_function_stat_reset_time', provolatile => 's', + proparallel => 'r', prorettype => 'timestamptz', proargtypes => 'oid', + prosrc => 'pg_stat_get_function_stat_reset_time' }, { oid => '3037', descr => 'statistics: number of scans done for table/index in current transaction', diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 8e8adb01176..d24bf864a22 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -212,7 +212,7 @@ typedef struct PgStat_TableXactStatus * ------------------------------------------------------------ */ -#define PGSTAT_FILE_FORMAT_ID 0x01A5BCB8 +#define PGSTAT_FILE_FORMAT_ID 0x01A5BCB9 typedef struct PgStat_ArchiverStats { @@ -384,6 +384,7 @@ typedef struct PgStat_StatFuncEntry PgStat_Counter total_time; /* times in microseconds */ PgStat_Counter self_time; + TimestampTz stat_reset_timestamp; } PgStat_StatFuncEntry; typedef struct PgStat_StatReplSlotEntry diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h index dc42d8043b5..4d2b8aa6081 100644 --- a/src/include/utils/pgstat_internal.h +++ b/src/include/utils/pgstat_internal.h @@ -691,6 +691,7 @@ extern void pgstat_database_reset_timestamp_cb(PgStatShared_Common *header, Time */ extern bool pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait); +extern void pgstat_function_reset_timestamp_cb(PgStatShared_Common *header, TimestampTz ts); /* diff --git a/src/test/isolation/expected/stats.out b/src/test/isolation/expected/stats.out index 8c7fe60217e..cfad309ccf3 100644 --- a/src/test/isolation/expected/stats.out +++ b/src/test/isolation/expected/stats.out @@ -1025,7 +1025,7 @@ test_stat_func| | | (1 row) -starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset s1_func_stats s1_func_stats2 s1_func_stats +starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check s1_func_stats_reset s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check pg_stat_force_next_flush ------------------------ @@ -1137,6 +1137,15 @@ name |pg_stat_get_function_calls|total_above_zero|self_above_zero test_stat_func| 3|t |t (1 row) +step s1_func_stats_reset_check: + SELECT pg_stat_get_function_stat_reset_time('test_stat_func'::regproc) + IS NOT NULL AS has_stats_reset; + +has_stats_reset +--------------- +f +(1 row) + step s1_func_stats_reset: SELECT pg_stat_reset_single_function_counters('test_stat_func'::regproc); pg_stat_reset_single_function_counters -------------------------------------- @@ -1185,6 +1194,15 @@ name |pg_stat_get_function_calls|total_above_zero|self_above_zero test_stat_func| 0|f |f (1 row) +step s1_func_stats_reset_check: + SELECT pg_stat_get_function_stat_reset_time('test_stat_func'::regproc) + IS NOT NULL AS has_stats_reset; + +has_stats_reset +--------------- +t +(1 row) + starting permutation: s1_func_stats_nonexistent s1_func_stats_reset_nonexistent s1_func_stats_nonexistent pg_stat_force_next_flush diff --git a/src/test/isolation/expected/stats_1.out b/src/test/isolation/expected/stats_1.out index 6b965bb9553..e1d937784cb 100644 --- a/src/test/isolation/expected/stats_1.out +++ b/src/test/isolation/expected/stats_1.out @@ -1025,7 +1025,7 @@ test_stat_func| | | (1 row) -starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset s1_func_stats s1_func_stats2 s1_func_stats +starting permutation: s1_track_funcs_all s2_track_funcs_all s1_func_call s2_func_call s2_func_call2 s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check s1_func_stats_reset s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check pg_stat_force_next_flush ------------------------ @@ -1137,6 +1137,15 @@ name |pg_stat_get_function_calls|total_above_zero|self_above_zero test_stat_func| 3|t |t (1 row) +step s1_func_stats_reset_check: + SELECT pg_stat_get_function_stat_reset_time('test_stat_func'::regproc) + IS NOT NULL AS has_stats_reset; + +has_stats_reset +--------------- +f +(1 row) + step s1_func_stats_reset: SELECT pg_stat_reset_single_function_counters('test_stat_func'::regproc); pg_stat_reset_single_function_counters -------------------------------------- @@ -1185,6 +1194,15 @@ name |pg_stat_get_function_calls|total_above_zero|self_above_zero test_stat_func| 0|f |f (1 row) +step s1_func_stats_reset_check: + SELECT pg_stat_get_function_stat_reset_time('test_stat_func'::regproc) + IS NOT NULL AS has_stats_reset; + +has_stats_reset +--------------- +t +(1 row) + starting permutation: s1_func_stats_nonexistent s1_func_stats_reset_nonexistent s1_func_stats_nonexistent pg_stat_force_next_flush diff --git a/src/test/isolation/specs/stats.spec b/src/test/isolation/specs/stats.spec index e6d29749915..da16710da0f 100644 --- a/src/test/isolation/specs/stats.spec +++ b/src/test/isolation/specs/stats.spec @@ -58,6 +58,10 @@ step s1_track_funcs_none { SET track_functions = 'none'; } step s1_func_call { SELECT test_stat_func(); } step s1_func_drop { DROP FUNCTION test_stat_func(); } step s1_func_stats_reset { SELECT pg_stat_reset_single_function_counters('test_stat_func'::regproc); } +step s1_func_stats_reset_check { + SELECT pg_stat_get_function_stat_reset_time('test_stat_func'::regproc) + IS NOT NULL AS has_stats_reset; +} step s1_func_stats_reset_nonexistent { SELECT pg_stat_reset_single_function_counters(12000); } step s1_reset { SELECT pg_stat_reset(); } step s1_func_stats { @@ -235,9 +239,9 @@ permutation s1_ff s2_ff s1_func_stats s2_func_call s2_func_call2 s2_ff - s1_func_stats s1_func_stats2 s1_func_stats + s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check s1_func_stats_reset - s1_func_stats s1_func_stats2 s1_func_stats + s1_func_stats s1_func_stats2 s1_func_stats s1_func_stats_reset_check # test pg_stat_reset_single_function_counters of non-existing function permutation diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7f1cb3bb4af..8859a5a885f 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2244,7 +2244,8 @@ pg_stat_user_functions| SELECT p.oid AS funcid, p.proname AS funcname, pg_stat_get_function_calls(p.oid) AS calls, pg_stat_get_function_total_time(p.oid) AS total_time, - pg_stat_get_function_self_time(p.oid) AS self_time + pg_stat_get_function_self_time(p.oid) AS self_time, + pg_stat_get_function_stat_reset_time(p.oid) AS stats_reset FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL));