From 1cdd58f24739fcc81522f14801c69cc1245345ec Mon Sep 17 00:00:00 2001
From: Ubuntu <ubuntu@ip-172-31-46-230.ec2.internal>
Date: Sat, 18 Oct 2025 18:51:25 +0000
Subject: [PATCH v1 1/1] Skip unregistered custom kinds on stats load

When pgstat.stat contains entries for a custom kind that is no
longer registered (e.g., the extension was removed from
shared_preload_libraries after a clean shutdown), the server
currently fails with "corrupted statistics file" and resets all
stats. Unlike built-in stats, custom kinds may legitimately be
unknown after restarts, so this behavior is too strict.

This patch records the lengths of all kinds in a small area at
the top of pgstat.stat during shutdown. On startup, these lengths
are used to skip past unregistered custom entries while emitting
a warning, instead of treating the file as corrupted.
---
 src/backend/utils/activity/pgstat.c | 103 +++++++++++++++++++++++++---
 1 file changed, 92 insertions(+), 11 deletions(-)

diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 7ef06150df7..56143de5a00 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -1578,6 +1578,7 @@ pgstat_write_statsfile(void)
 	const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
 	dshash_seq_status hstat;
 	PgStatShared_HashEntry *ps;
+	size_t		kind_lengths[PGSTAT_KIND_MAX] = {0};
 
 	pgstat_assert_is_up();
 
@@ -1608,6 +1609,20 @@ pgstat_write_statsfile(void)
 	format_id = PGSTAT_FILE_FORMAT_ID;
 	write_chunk_s(fpout, &format_id);
 
+	/* Write the lengths of all stats kinds */
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		size_t		kind_len = 0;
+
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info)
+			kind_len = pgstat_get_entry_len(kind);
+
+		kind_lengths[kind - 1] = kind_len;
+		write_chunk_s(fpout, &kind_len);
+	}
+
 	/* Write various stats structs for fixed number of objects */
 	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
 	{
@@ -1703,7 +1718,7 @@ pgstat_write_statsfile(void)
 		/* Write except the header part of the entry */
 		write_chunk(fpout,
 					pgstat_get_entry_data(ps->key.kind, shstats),
-					pgstat_get_entry_len(ps->key.kind));
+					kind_lengths[ps->key.kind - 1]);
 	}
 	dshash_seq_term(&hstat);
 
@@ -1761,6 +1776,8 @@ pgstat_read_statsfile(void)
 	bool		found;
 	const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
 	PgStat_ShmemControl *shmem = pgStatLocal.shmem;
+	size_t		kind_lengths[PGSTAT_KIND_MAX] = {0};
+	bool		kind_warnings[PGSTAT_KIND_MAX] = {false};
 
 	/* shouldn't be called from postmaster */
 	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
@@ -1803,6 +1820,17 @@ pgstat_read_statsfile(void)
 		goto error;
 	}
 
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		size_t	   *kind_length = &kind_lengths[kind - 1];
+
+		if (!read_chunk_s(fpin, kind_length))
+		{
+			elog(WARNING, "could not read kind lengths");
+			goto error;
+		}
+	}
+
 	/*
 	 * We found an existing statistics file. Read it and put all the stats
 	 * data into place.
@@ -1836,9 +1864,34 @@ pgstat_read_statsfile(void)
 					info = pgstat_get_kind_info(kind);
 					if (!info)
 					{
-						elog(WARNING, "could not find information of kind %u for entry of type %c",
-							 kind, t);
-						goto error;
+						if (pgstat_is_kind_custom(kind))
+						{
+							size_t		kind_length = kind_lengths[kind - 1];
+
+							if (!kind_warnings[kind - 1])
+							{
+
+								kind_warnings[kind - 1] = true;
+
+								elog(WARNING, "found unregistered custom stats kind %u for entry of type %c, skipping",
+									 kind, t);
+							}
+
+							if (fseek(fpin, kind_lengths[kind - 1], SEEK_CUR) != 0)
+							{
+								elog(WARNING, "could not seek %lu bytes of stats kind %u for entry of type %c",
+									 (unsigned long) kind_length, kind, t);
+								goto error;
+							}
+
+							continue;
+						}
+						else
+						{
+							elog(WARNING, "could not find information of kind %u for entry of type %c",
+								 kind, t);
+							goto error;
+						}
 					}
 
 					if (!info->fixed_amount)
@@ -1875,6 +1928,7 @@ pgstat_read_statsfile(void)
 					PgStat_HashKey key;
 					PgStatShared_HashEntry *p;
 					PgStatShared_Common *header;
+					const PgStat_KindInfo *info;
 
 					CHECK_FOR_INTERRUPTS();
 
@@ -1895,12 +1949,39 @@ pgstat_read_statsfile(void)
 							goto error;
 						}
 
-						if (!pgstat_get_kind_info(key.kind))
+						info = pgstat_get_kind_info(key.kind);
+						if (!info)
 						{
-							elog(WARNING, "could not find information of kind for entry %u/%u/%" PRIu64 " of type %c",
-								 key.kind, key.dboid,
-								 key.objid, t);
-							goto error;
+							if (pgstat_is_kind_custom(key.kind))
+							{
+								size_t		kind_length = kind_lengths[key.kind - 1];
+
+
+								if (!kind_warnings[key.kind - 1])
+								{
+
+									kind_warnings[key.kind - 1] = true;
+									elog(WARNING, "invalid custom stats kind %u for entry of type %c, skipping",
+										 key.kind, t);
+								}
+
+								if (fseek(fpin, kind_lengths[key.kind - 1], SEEK_CUR) != 0)
+								{
+									elog(WARNING, "could not seek %lu bytes of stats kind %u for entry of type %c",
+										 (unsigned long) kind_length, key.kind, t);
+									goto error;
+								}
+
+								continue;
+							}
+							else
+							{
+
+								elog(WARNING, "could not find information of kind for entry %u/%u/%" PRIu64 " of type %c",
+									 key.kind, key.dboid,
+									 key.objid, t);
+								goto error;
+							}
 						}
 					}
 					else
@@ -1946,7 +2027,7 @@ pgstat_read_statsfile(void)
 						if (!kind_info->from_serialized_name(&name, &key))
 						{
 							/* skip over data for entry we don't care about */
-							if (fseek(fpin, pgstat_get_entry_len(kind), SEEK_CUR) != 0)
+							if (fseek(fpin, kind_lengths[kind - 1], SEEK_CUR) != 0)
 							{
 								elog(WARNING, "could not seek \"%s\" of stats kind %u for entry of type %c",
 									 NameStr(name), kind, t);
@@ -1992,7 +2073,7 @@ pgstat_read_statsfile(void)
 
 					if (!read_chunk(fpin,
 									pgstat_get_entry_data(key.kind, header),
-									pgstat_get_entry_len(key.kind)))
+									kind_lengths[key.kind - 1]))
 					{
 						elog(WARNING, "could not read data for entry %u/%u/%" PRIu64 " of type %c",
 							 key.kind, key.dboid,
-- 
2.43.0

