From 9c3701105530ca36112a8a30e7f7d14b6fc36936 Mon Sep 17 00:00:00 2001 From: Zsolt Parragi Date: Wed, 14 Oct 2026 16:39:34 +0000 Subject: [PATCH v1] Fix crash at startup with stats file entry of fixed-numbered stats kind pgstat_read_statsfile() did not check that the kind of a hash or name entry is variable-numbered. Since 44539786b829, fixed-numbered kinds have no hash table and no DSA area, so such an entry crashes the startup process, and again on each restart as the stats file is not removed. This can happen if a custom kind ID is reused by a fixed-numbered kind. This commit discards the stats file in this case, like fixed-numbered entries already do for variable-numbered kinds. A GUC is added to test_custom_fixed_stats to set its kind ID, for test coverage. Oversight in 44539786b829. --- src/backend/utils/activity/pgstat.c | 7 ++++ .../test_custom_stats/t/001_custom_stats.pl | 39 +++++++++++++++++++ .../test_custom_fixed_stats.c | 33 +++++++++++----- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c index c393bf7d59e..dea2db48e48 100644 --- a/src/backend/utils/activity/pgstat.c +++ b/src/backend/utils/activity/pgstat.c @@ -2104,6 +2104,13 @@ pgstat_read_statsfile(void) Assert(key.kind == kind); } + if (kind_info->fixed_amount) + { + elog(WARNING, "invalid fixed_amount in stats kind %u for entry of type %c", + key.kind, t); + goto error; + } + /* * This intentionally doesn't use pgstat_get_entry_ref() - * putting all stats into checkpointer's diff --git a/src/test/modules/test_custom_stats/t/001_custom_stats.pl b/src/test/modules/test_custom_stats/t/001_custom_stats.pl index 88dce8caffb..551d922c2f0 100644 --- a/src/test/modules/test_custom_stats/t/001_custom_stats.pl +++ b/src/test/modules/test_custom_stats/t/001_custom_stats.pl @@ -9,6 +9,7 @@ # - Loss after crash recovery. # - Resets for fixed-sized stats. # - Custom hash tables for variable-sized stats +# - Stats file with entries of a kind ID now used by a fixed-sized kind use strict; use warnings FATAL => 'all'; @@ -216,5 +217,43 @@ $result = $node->safe_psql('postgres', ); is($result, "0", "report of fixed-sized after manual reset"); +# Check that entries of a variable-sized stats kind in the stats file are +# discarded if their kind ID is used by a fixed-sized stats kind at the +# next startup. +$node->append_conf('postgresql.conf', "test_custom_fixed_stats.kind_id = 25"); +foreach my $stats_kind (@var_stats_kinds) +{ + my $kind_name = $stats_kind->{name}; + + # Load only the variable-sized kind, so as the stats file includes only + # entries for it. + $node->adjust_conf('postgresql.conf', 'shared_preload_libraries', + "'test_custom_var_stats'"); + $node->adjust_conf('postgresql.conf', 'test_custom_var_stats.kind', + "'$kind_name'"); + $node->restart; + + $node->safe_psql('postgres', + q(select test_custom_stats_var_create('entry1', 'Test entry 1'))); + $node->safe_psql('postgres', + q(select test_custom_stats_var_update('entry1'))); + $node->stop; + + # Use the ID of the variable-sized kind for the fixed-sized kind. + $node->adjust_conf('postgresql.conf', 'shared_preload_libraries', + "'test_custom_fixed_stats'"); + my $log_offset = -s $node->logfile; + $node->start; + + ok( $node->log_contains( + qr/invalid fixed_amount in stats kind 25 for entry of type S/, + $log_offset), + "stats file discarded with kind 25 now fixed-sized for $kind_name"); + + $result = $node->safe_psql('postgres', + q(select numcalls from test_custom_stats_fixed_report())); + is($result, "0", "fixed-sized stats usable with kind 25 for $kind_name"); +} + # Test completed successfully done_testing(); diff --git a/src/test/modules/test_custom_stats/test_custom_fixed_stats.c b/src/test/modules/test_custom_stats/test_custom_fixed_stats.c index 32be1d587a1..ca63667f203 100644 --- a/src/test/modules/test_custom_stats/test_custom_fixed_stats.c +++ b/src/test/modules/test_custom_stats/test_custom_fixed_stats.c @@ -17,6 +17,7 @@ #include "funcapi.h" #include "pgstat.h" #include "utils/builtins.h" +#include "utils/guc.h" #include "utils/pgstat_internal.h" #include "utils/timestamp.h" @@ -60,9 +61,10 @@ static const PgStat_KindInfo custom_stats = { }; /* - * Kind ID for test_custom_fixed_stats. + * Kind ID for test_custom_fixed_stats, configurable so as it can conflict + * with the ID of test_custom_var_stats. */ -#define PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS 26 +static int test_custom_fixed_stats_kind = 26; /*-------------------------------------------------------------------------- * Module initialization @@ -72,8 +74,19 @@ static const PgStat_KindInfo custom_stats = { void _PG_init(void) { + DefineCustomIntVariable("test_custom_fixed_stats.kind_id", + "Sets the ID of the custom fixed stats kind.", + NULL, + &test_custom_fixed_stats_kind, + 26, + PGSTAT_KIND_CUSTOM_MIN, + PGSTAT_KIND_CUSTOM_MAX, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + /* Register custom statistics kind */ - pgstat_register_kind(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS, &custom_stats); + pgstat_register_kind(test_custom_fixed_stats_kind, &custom_stats); } /* @@ -97,7 +110,7 @@ static void test_custom_stats_fixed_reset_all_cb(TimestampTz ts) { PgStatShared_CustomFixedEntry *stats_shmem = - pgstat_get_custom_shmem_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + pgstat_get_custom_shmem_data(test_custom_fixed_stats_kind); /* see explanation above PgStatShared_Archiver for the reset protocol */ LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); @@ -117,9 +130,9 @@ static void test_custom_stats_fixed_snapshot_cb(void) { PgStatShared_CustomFixedEntry *stats_shmem = - pgstat_get_custom_shmem_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + pgstat_get_custom_shmem_data(test_custom_fixed_stats_kind); PgStat_StatCustomFixedEntry *stat_snap = - pgstat_get_custom_snapshot_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + pgstat_get_custom_snapshot_data(test_custom_fixed_stats_kind); PgStat_StatCustomFixedEntry *reset_offset = &stats_shmem->reset_offset; PgStat_StatCustomFixedEntry reset; @@ -153,7 +166,7 @@ test_custom_stats_fixed_update(PG_FUNCTION_ARGS) { PgStatShared_CustomFixedEntry *stats_shmem; - stats_shmem = pgstat_get_custom_shmem_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + stats_shmem = pgstat_get_custom_shmem_data(test_custom_fixed_stats_kind); LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); @@ -174,7 +187,7 @@ PG_FUNCTION_INFO_V1(test_custom_stats_fixed_reset); Datum test_custom_stats_fixed_reset(PG_FUNCTION_ARGS) { - pgstat_reset_of_kind(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + pgstat_reset_of_kind(test_custom_fixed_stats_kind); PG_RETURN_VOID(); } @@ -193,8 +206,8 @@ test_custom_stats_fixed_report(PG_FUNCTION_ARGS) PgStat_StatCustomFixedEntry *stats; /* Take snapshot (applies reset offsets) */ - pgstat_snapshot_fixed(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); - stats = pgstat_get_custom_snapshot_data(PGSTAT_KIND_TEST_CUSTOM_FIXED_STATS); + pgstat_snapshot_fixed(test_custom_fixed_stats_kind); + stats = pgstat_get_custom_snapshot_data(test_custom_fixed_stats_kind); /* Build return tuple */ tupdesc = CreateTemplateTupleDesc(2); -- 2.56.0