From 87d34f2c618798efb37f7e225a753d1e3c69b11c Mon Sep 17 00:00:00 2001
From: Sami Imseih <simseih@amazon.com>
Date: Mon, 10 Nov 2025 00:03:41 -0600
Subject: [PATCH v4 2/2] Allow cumulative statistics to serialize auxiliary
 data to disk.

Cumulative Statistics kinds can now write additional per-entry data to
the statistics file that doesn't fit in shared memory. This is useful
for statistics with variable-length auxiliary data.

Three new optional callbacks are added to PgStat_KindInfo:

* to_serialized_extra_stats: writes auxiliary data for an entry
* from_serialized_extra_stats: reads auxiliary data for an entry
* end_extra_stats: performs cleanup after read/write/discard operations

All three callbacks must be provided together to ensure the reader
consumes exactly what the writer produces. The end_extra_stats callback
is invoked after processing all entries of a kind, allowing extensions
to close file handles and clean up resources.

Tests are also added to test_custom_stats.

Discussion: https://www.postgresql.org/message-id/flat/CAA5RZ0s9SDOu+Z6veoJCHWk+kDeTktAtC-KY9fQ9Z6BJdDUirQ@mail.gmail.com
---
 src/backend/utils/activity/pgstat.c           |  81 ++++-
 src/include/utils/pgstat_internal.h           |  37 ++
 .../test_custom_stats/t/001_custom_stats.pl   |  20 +-
 .../test_custom_var_stats--1.0.sql            |   6 +-
 .../test_custom_stats/test_custom_var_stats.c | 327 +++++++++++++++++-
 5 files changed, 447 insertions(+), 24 deletions(-)

diff --git a/src/backend/utils/activity/pgstat.c b/src/backend/utils/activity/pgstat.c
index 8713c7a0483..8804c1688c5 100644
--- a/src/backend/utils/activity/pgstat.c
+++ b/src/backend/utils/activity/pgstat.c
@@ -194,6 +194,7 @@ static void pgstat_build_snapshot(void);
 static void pgstat_build_snapshot_fixed(PgStat_Kind kind);
 
 static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
+static inline void pgstat_check_extra_callbacks(const PgStat_KindInfo *kind_info, int elevel);
 
 
 /* ----------
@@ -523,6 +524,7 @@ pgstat_discard_stats(void)
 
 	/* NB: this needs to be done even in single user mode */
 
+	/* First, cleanup the main stats file, PGSTAT_STAT_PERMANENT_FILENAME */
 	ret = unlink(PGSTAT_STAT_PERMANENT_FILENAME);
 	if (ret != 0)
 	{
@@ -544,6 +546,15 @@ pgstat_discard_stats(void)
 								 PGSTAT_STAT_PERMANENT_FILENAME)));
 	}
 
+	/* Let each stats kind run its cleanup callback, if it provides one */
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info && kind_info->end_extra_stats)
+			kind_info->end_extra_stats(STATS_DISCARD);
+	}
+
 	/*
 	 * Reset stats contents. This will set reset timestamps of fixed-numbered
 	 * stats to the current time (no variable stats exist).
@@ -645,6 +656,10 @@ pgstat_initialize(void)
 
 	pgstat_attach_shmem();
 
+	/* Check a kind's extra-data callback setup */
+	for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN; kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
+		pgstat_check_extra_callbacks(&pgstat_kind_builtin_infos[kind], PANIC);
+
 	pgstat_init_snapshot_fixed();
 
 	/* Backend initialization callbacks */
@@ -1432,6 +1447,32 @@ pgstat_is_kind_valid(PgStat_Kind kind)
 	return pgstat_is_kind_builtin(kind) || pgstat_is_kind_custom(kind);
 }
 
+/*
+ * Validate that extra stats callbacks are all provided together or not at all.
+ * Reports error at specified level if validation fails.
+ */
+static inline void
+pgstat_check_extra_callbacks(const PgStat_KindInfo *kind_info, int elevel)
+{
+	bool		has_extra;
+
+	has_extra =
+		kind_info->to_serialized_extra_stats ||
+		kind_info->from_serialized_extra_stats ||
+		kind_info->end_extra_stats;
+
+	if (has_extra &&
+		(!kind_info->to_serialized_extra_stats ||
+		 !kind_info->from_serialized_extra_stats ||
+		 !kind_info->end_extra_stats))
+	{
+		ereport(elevel,
+				(errmsg("incomplete serialization callbacks for statistics kind \"%s\"",
+						kind_info->name),
+				 errdetail("callbacks to_serialized_extra_stats, from_serialized_extra_stats, and end_extra_stats must be provided together.")));
+	}
+}
+
 const PgStat_KindInfo *
 pgstat_get_kind_info(PgStat_Kind kind)
 {
@@ -1525,6 +1566,9 @@ pgstat_register_kind(PgStat_Kind kind, const PgStat_KindInfo *kind_info)
 					 errdetail("Existing cumulative statistics with ID %u has the same name.", existing_kind)));
 	}
 
+	/* Check a kind's extra-data callback setup */
+	pgstat_check_extra_callbacks(kind_info, ERROR);
+
 	/* Register it */
 	pgstat_kind_custom_infos[idx] = kind_info;
 	ereport(LOG,
@@ -1702,6 +1746,9 @@ pgstat_write_statsfile(void)
 		pgstat_write_chunk(fpout,
 						   pgstat_get_entry_data(ps->key.kind, shstats),
 						   pgstat_get_entry_len(ps->key.kind));
+
+		if (kind_info->to_serialized_extra_stats)
+			kind_info->to_serialized_extra_stats(&ps->key, shstats, fpout);
 	}
 	dshash_seq_term(&hstat);
 
@@ -1734,6 +1781,15 @@ pgstat_write_statsfile(void)
 		/* durable_rename already emitted log message */
 		unlink(tmpfile);
 	}
+
+	/* Now, allow kinds to finalize the writes for the extra files */
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info && kind_info->end_extra_stats)
+			kind_info->end_extra_stats(STATS_WRITE);
+	}
 }
 
 /* helper for pgstat_read_statsfile() */
@@ -1871,6 +1927,7 @@ pgstat_read_statsfile(void)
 					PgStat_HashKey key;
 					PgStatShared_HashEntry *p;
 					PgStatShared_Common *header;
+					const PgStat_KindInfo *kind_info = NULL;
 
 					CHECK_FOR_INTERRUPTS();
 
@@ -1891,7 +1948,8 @@ pgstat_read_statsfile(void)
 							goto error;
 						}
 
-						if (!pgstat_get_kind_info(key.kind))
+						kind_info = pgstat_get_kind_info(key.kind);
+						if (!kind_info)
 						{
 							elog(WARNING, "could not find information of kind for entry %u/%u/%" PRIu64 " of type %c",
 								 key.kind, key.dboid,
@@ -1902,7 +1960,6 @@ pgstat_read_statsfile(void)
 					else
 					{
 						/* stats entry identified by name on disk (e.g. slots) */
-						const PgStat_KindInfo *kind_info = NULL;
 						PgStat_Kind kind;
 						NameData	name;
 
@@ -1996,6 +2053,16 @@ pgstat_read_statsfile(void)
 						goto error;
 					}
 
+					if (kind_info->from_serialized_extra_stats)
+					{
+						if (!kind_info->from_serialized_extra_stats(&key, header, fpin))
+						{
+							elog(WARNING, "could not read extra stats for entry %u/%u/%" PRIu64,
+								 key.kind, key.dboid, key.objid);
+							goto error;
+						}
+					}
+
 					break;
 				}
 			case PGSTAT_FILE_ENTRY_END:
@@ -2019,11 +2086,21 @@ pgstat_read_statsfile(void)
 	}
 
 done:
+	/* First, cleanup the main stats file, PGSTAT_STAT_PERMANENT_FILENAME */
 	FreeFile(fpin);
 
 	elog(DEBUG2, "removing permanent stats file \"%s\"", statfile);
 	unlink(statfile);
 
+	/* Let each stats kind run its cleanup callback, if it provides one */
+	for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
+	{
+		const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
+
+		if (kind_info && kind_info->end_extra_stats)
+			kind_info->end_extra_stats(STATS_READ);
+	}
+
 	return;
 
 error:
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index ca1ba6420ca..48b40816570 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -63,6 +63,20 @@ typedef struct PgStat_HashKey
 								 * identifier. */
 } PgStat_HashKey;
 
+/*
+ * Tracks if the stats file is being read, written or discarded.
+ *
+ * These states allow plugins that create extra statistics files
+ * to determine the current operation and perform any necessary
+ * file cleanup.
+ */
+typedef enum PgStat_StatsFileOp
+{
+	STATS_WRITE,
+	STATS_READ,
+	STATS_DISCARD,
+}			PgStat_StatsFileOp;
+
 /*
  * PgStat_HashKey should not have any padding.  Checking that the structure
  * size matches with the sum of each field is a check simple enough to
@@ -303,6 +317,29 @@ typedef struct PgStat_KindInfo
 									   const PgStatShared_Common *header, NameData *name);
 	bool		(*from_serialized_name) (const NameData *name, PgStat_HashKey *key);
 
+	/*
+	 * Optional callbacks for kinds that write additional per-entry data to
+	 * the stats file.  If any of these callbacks are provided, all three must
+	 * be provided to ensure that the reader consumes exactly the data written
+	 * by the writer.
+	 *
+	 * to_serialized_extra_stats: write extra data for an entry.
+	 *
+	 * from_serialized_extra_stats: read the extra data for an entry. Returns
+	 * true on success, false on read error.
+	 *
+	 * end_extra_stats: invoked once per operation (read, write, discard)
+	 * after all entries of this kind have been processed.
+	 *
+	 * Note: statfile is a pointer to the main stats file,
+	 * PGSTAT_STAT_PERMANENT_FILENAME.
+	 */
+	void		(*to_serialized_extra_stats) (const PgStat_HashKey *key,
+											  const PgStatShared_Common *header, FILE *statfile);
+	bool		(*from_serialized_extra_stats) (const PgStat_HashKey *key,
+												const PgStatShared_Common *header, FILE *statfile);
+	void		(*end_extra_stats) (PgStat_StatsFileOp status);
+
 	/*
 	 * For fixed-numbered statistics: Initialize shared memory state.
 	 *
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 c4fceb7d267..55a8956a0d9 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
@@ -26,10 +26,10 @@ $node->safe_psql('postgres', q(CREATE EXTENSION test_custom_var_stats));
 $node->safe_psql('postgres', q(CREATE EXTENSION test_custom_fixed_stats));
 
 # Create variable statistics entries
-$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry1')));
-$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry2')));
-$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry3')));
-$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry4')));
+$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry1', 'Test entry 1')));
+$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry2', 'Test entry 2')));
+$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry3', 'Test entry 3')));
+$node->safe_psql('postgres', q(select pgstat_create_custom_var_stats('entry4', 'Test entry 4')));
 
 # Update counters: entry1=2, entry2=3, entry3=2, entry4=3, fixed=3
 $node->safe_psql('postgres', q(select pgstat_update_custom_var_stats('entry1')));
@@ -48,16 +48,16 @@ $node->safe_psql('postgres', q(select pgstat_update_custom_fixed_stats()));
 
 # Test variable statistics reporting
 my $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry1')));
-is($result, "entry1|2", "var stats entry1 reports correct calls");
+is($result, "entry1|2|Test entry 1", "var stats entry1 reports correct calls");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry2')));
-is($result, "entry2|3", "var stats entry2 reports correct calls");
+is($result, "entry2|3|Test entry 2", "var stats entry2 reports correct calls");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry3')));
-is($result, "entry3|2", "var stats entry3 reports correct calls");
+is($result, "entry3|2|Test entry 3", "var stats entry3 reports correct calls");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry4')));
-is($result, "entry4|3", "var stats entry4 reports correct calls");
+is($result, "entry4|3|Test entry 4", "var stats entry4 reports correct calls");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_fixed_stats()));
 is($result, "3|", "fixed stats reports correct calls");
@@ -76,10 +76,10 @@ $node->stop();
 $node->start();
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry1')));
-is($result, "entry1|2", "var stats entry1 persists after clean restart");
+is($result, "entry1|2|Test entry 1", "var stats entry1 persists after clean restart");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_var_stats('entry2')));
-is($result, "entry2|3", "var stats entry2 persists after clean restart");
+is($result, "entry2|3|Test entry 2", "var stats entry2 persists after clean restart");
 
 $result = $node->safe_psql('postgres', q(select * from pgstat_report_custom_fixed_stats()));
 is($result, "3|", "fixed stats persists after clean restart");
diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql b/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql
index 84ae2bf5666..c509567de7d 100644
--- a/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql
+++ b/src/test/modules/test_custom_stats/test_custom_var_stats--1.0.sql
@@ -3,7 +3,7 @@
 -- complain if script is sourced in psql, rather than via CREATE EXTENSION
 \echo Use "CREATE EXTENSION test_custom_var_stats" to load this file. \quit
 
-CREATE FUNCTION pgstat_create_custom_var_stats(IN name TEXT)
+CREATE FUNCTION pgstat_create_custom_var_stats(IN name TEXT, in description TEXT)
 RETURNS void
 AS 'MODULE_PATHNAME', 'pgstat_create_custom_var_stats'
 LANGUAGE C STRICT PARALLEL UNSAFE;
@@ -19,7 +19,9 @@ AS 'MODULE_PATHNAME', 'pgstat_drop_custom_var_stats'
 LANGUAGE C STRICT PARALLEL UNSAFE;
 
 
-CREATE FUNCTION pgstat_report_custom_var_stats(INOUT name TEXT, OUT calls BIGINT)
+CREATE FUNCTION pgstat_report_custom_var_stats(INOUT name TEXT,
+                                               OUT calls BIGINT,
+                                               OUT description TEXT)
 RETURNS SETOF record
 AS 'MODULE_PATHNAME', 'pgstat_report_custom_var_stats'
 LANGUAGE C STRICT PARALLEL UNSAFE;
diff --git a/src/test/modules/test_custom_stats/test_custom_var_stats.c b/src/test/modules/test_custom_stats/test_custom_var_stats.c
index 6320eaf2cae..6d755904df5 100644
--- a/src/test/modules/test_custom_stats/test_custom_var_stats.c
+++ b/src/test/modules/test_custom_stats/test_custom_var_stats.c
@@ -14,6 +14,7 @@
 
 #include "common/hashfn.h"
 #include "funcapi.h"
+#include "storage/dsm_registry.h"
 #include "utils/builtins.h"
 #include "utils/pgstat_internal.h"
 
@@ -33,6 +34,9 @@ PG_MODULE_MAGIC_EXT(
  */
 #define PGSTAT_KIND_TEST_CUSTOM_VAR_STATS 25
 
+/* File paths for extra statistics data serialization */
+#define PGSTAT_CUSTOM_EXTRA_DATA_DESC "pg_stat/test_custom_var_stats_desc.stats"
+
 /*
  * Hash statistic name to generate entry index for pgstat lookup.
  */
@@ -54,15 +58,41 @@ typedef struct PgStatShared_CustomEntry
 {
 	PgStatShared_Common header; /* standard pgstat entry header */
 	PgStat_StatCustomEntry stats;	/* custom statistics data */
+	dsa_pointer description;	/* extra statistics data */
 }			PgStatShared_CustomEntry;
 
+/*--------------------------------------------------------------------------
+ * Global Variables
+ *--------------------------------------------------------------------------
+ */
+
+/* File handle for extra statistics data serialization */
+static FILE *fd_description = NULL;
+
+/* Current write offset in fd_description file */
+static long fd_description_offset = 0;
+
+/* DSA area for storing variable-length description strings */
+dsa_area   *custom_stats_description_dsa = NULL;
+
 /*--------------------------------------------------------------------------
  * Function prototypes
  *--------------------------------------------------------------------------
  */
 
 /* Flush callback: merge pending stats into shared memory */
-static bool pgstat_custom_entry_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+static bool pgstat_custom_var_entry_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
+
+/* Serialization callback: serialize extra statistics data */
+static void pgstat_custom_var_stats_serialize(const PgStat_HashKey *key,
+											  const PgStatShared_Common *header, FILE *statfile);
+
+/* Deserialization callback: deserialize extra statistics data */
+static bool pgstat_custom_var_stats_deserialize(const PgStat_HashKey *key,
+												const PgStatShared_Common *header, FILE *statfile);
+
+/* Cleanup callback: end of statistics file operations */
+static void pgstat_custom_var_stats_file_cleanup(PgStat_StatsFileOp status);
 
 /*--------------------------------------------------------------------------
  * Custom kind configuration
@@ -79,7 +109,10 @@ static const PgStat_KindInfo custom_stats = {
 	.shared_data_off = offsetof(PgStatShared_CustomEntry, stats),
 	.shared_data_len = sizeof(((PgStatShared_CustomEntry *) 0)->stats),
 	.pending_size = sizeof(PgStat_StatCustomEntry),
-	.flush_pending_cb = pgstat_custom_entry_flush_cb,
+	.flush_pending_cb = pgstat_custom_var_entry_flush_cb,
+	.to_serialized_extra_stats = pgstat_custom_var_stats_serialize,
+	.from_serialized_extra_stats = pgstat_custom_var_stats_deserialize,
+	.end_extra_stats = pgstat_custom_var_stats_file_cleanup,
 };
 
 /*--------------------------------------------------------------------------
@@ -113,7 +146,7 @@ _PG_init(void)
  * Returns false only if nowait=true and lock acquisition fails.
  */
 static bool
-pgstat_custom_entry_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
+pgstat_custom_var_entry_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 {
 	PgStat_StatCustomEntry *pending_entry;
 	PgStatShared_CustomEntry *shared_entry;
@@ -132,6 +165,234 @@ pgstat_custom_entry_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
 	return true;
 }
 
+/*
+ * pgstat_custom_var_stats_serialize() -
+ *
+ * Serialize extra data (descriptions) for custom statistics entries to
+ * the statistics file. Called during statistics file writing to preserve
+ * description strings across restarts.
+ */
+static void
+pgstat_custom_var_stats_serialize(const PgStat_HashKey *key,
+								  const PgStatShared_Common *header, FILE *statfile)
+{
+	char	   *description;
+	size_t		len;
+	long		offset;
+	PgStatShared_CustomEntry *entry = (PgStatShared_CustomEntry *) header;
+	bool		found;
+
+	if (!custom_stats_description_dsa)
+		custom_stats_description_dsa = GetNamedDSA("pgstat_custom_stat_dsa", &found);
+
+	/* Open statistics file for writing if not already open */
+	if (!fd_description)
+	{
+		fd_description = AllocateFile(PGSTAT_CUSTOM_EXTRA_DATA_DESC, PG_BINARY_W);
+		if (fd_description == NULL)
+		{
+			ereport(LOG,
+					(errcode_for_file_access(),
+					 errmsg("could not open statistics file \"%s\" for writing: %m",
+							PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+			len = 0;
+			offset = 0;
+			fwrite(&len, sizeof(len), 1, statfile);
+			fwrite(&offset, sizeof(offset), 1, statfile);
+			return;
+		}
+		fd_description_offset = 0;
+	}
+
+	/* Handle entries without descriptions */
+	if (!DsaPointerIsValid(entry->description) || !custom_stats_description_dsa)
+	{
+		len = 0;
+		offset = 0;
+		fwrite(&len, sizeof(len), 1, statfile);
+		fwrite(&offset, sizeof(offset), 1, statfile);
+		return;
+	}
+
+	/* Get current offset in fd_description */
+	offset = fd_description_offset;
+
+	/* Retrieve description from DSA and write to fd_description */
+	description = dsa_get_address(custom_stats_description_dsa, entry->description);
+	len = strlen(description) + 1;
+	fwrite(description, 1, len, fd_description);
+	fd_description_offset += len;
+
+	/* Write length and offset to statfile */
+	fwrite(&len, sizeof(len), 1, statfile);
+	fwrite(&offset, sizeof(offset), 1, statfile);
+}
+
+/*
+ * pgstat_custom_var_stats_deserialize() -
+ *
+ * Deserialize extra data (descriptions) for custom statistics entries from
+ * the statistics file. Called during statistics file reading to restore
+ * description strings after a restart.
+ */
+static bool
+pgstat_custom_var_stats_deserialize(const PgStat_HashKey *key,
+									const PgStatShared_Common *header, FILE *statfile)
+{
+	PgStatShared_CustomEntry *entry;
+	dsa_pointer dp;
+	size_t		len;
+	long		offset;
+	char	   *buffer;
+	bool		found;
+
+	/* Read length and offset from statfile */
+	if (fread(&len, sizeof(len), 1, statfile) != 1 ||
+		fread(&offset, sizeof(offset), 1, statfile) != 1)
+	{
+		elog(WARNING, "failed to read description metadata from statistics file");
+		return false;
+	}
+
+	entry = (PgStatShared_CustomEntry *) header;
+
+	/* Handle empty descriptions */
+	if (len == 0)
+	{
+		entry->description = InvalidDsaPointer;
+		return true;
+	}
+
+	/* Initialize DSA if needed */
+	if (!custom_stats_description_dsa)
+		custom_stats_description_dsa = GetNamedDSA("pgstat_custom_stat_dsa", &found);
+
+	if (!custom_stats_description_dsa)
+	{
+		elog(WARNING, "could not access DSA for custom statistics descriptions");
+		return false;
+	}
+
+	/* Open statistics file for reading if not already open */
+	if (!fd_description)
+	{
+		fd_description = AllocateFile(PGSTAT_CUSTOM_EXTRA_DATA_DESC, PG_BINARY_R);
+		if (fd_description == NULL)
+		{
+			if (errno != ENOENT)
+				ereport(LOG,
+						(errcode_for_file_access(),
+						 errmsg("could not open statistics file \"%s\" for reading: %m",
+								PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+			pgstat_reset_of_kind(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS);
+			return false;
+		}
+	}
+
+	/* Seek to the offset and read description */
+	if (fseek(fd_description, offset, SEEK_SET) != 0)
+	{
+		elog(WARNING, "failed to seek to offset %ld in description file", offset);
+		return false;
+	}
+
+	buffer = palloc(len);
+	if (fread(buffer, 1, len, fd_description) != len)
+	{
+		pfree(buffer);
+		elog(WARNING, "failed to read description from file");
+		return false;
+	}
+
+	/* Allocate space in DSA and copy the description */
+	dp = dsa_allocate(custom_stats_description_dsa, len);
+	memcpy(dsa_get_address(custom_stats_description_dsa, dp), buffer, len);
+	entry->description = dp;
+
+	pfree(buffer);
+
+	return true;
+}
+
+/*
+ * pgstat_custom_var_stats_file_cleanup() -
+ *
+ * Cleanup function called at the end of statistics file operations.
+ * Handles closing files and cleanup based on the operation type.
+ */
+static void
+pgstat_custom_var_stats_file_cleanup(PgStat_StatsFileOp status)
+{
+	switch (status)
+	{
+		case STATS_WRITE:
+			if (!fd_description)
+				return;
+
+			fd_description_offset = 0;
+
+			/* Check for write errors and cleanup if necessary */
+			if (ferror(fd_description))
+			{
+				ereport(LOG,
+						(errcode_for_file_access(),
+						 errmsg("could not write statistics file \"%s\": %m",
+								PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+				FreeFile(fd_description);
+				unlink(PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+			}
+			else if (FreeFile(fd_description) < 0)
+			{
+				ereport(LOG,
+						(errcode_for_file_access(),
+						 errmsg("could not close statistics file \"%s\": %m",
+								PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+				unlink(PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+			}
+			break;
+
+		case STATS_READ:
+			if (!fd_description)
+				return;
+
+			FreeFile(fd_description);
+
+			/* Remove the temporary statistics file after reading */
+			elog(DEBUG2, "removing statistics file \"%s\"", PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+			unlink(PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+			break;
+
+		case STATS_DISCARD:
+			{
+				int			ret;
+
+				/* Attempt to remove the statistics file */
+				ret = unlink(PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+				if (ret != 0)
+				{
+					if (errno == ENOENT)
+						elog(LOG,
+							 "didn't need to unlink permanent stats file \"%s\" - didn't exist",
+							 PGSTAT_CUSTOM_EXTRA_DATA_DESC);
+					else
+						ereport(LOG,
+								(errcode_for_file_access(),
+								 errmsg("could not unlink permanent statistics file \"%s\": %m",
+										PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+				}
+				else
+				{
+					ereport(LOG,
+							(errmsg_internal("unlinked permanent statistics file \"%s\"",
+											 PGSTAT_CUSTOM_EXTRA_DATA_DESC)));
+				}
+			}
+			break;
+	}
+
+	fd_description = NULL;
+}
+
 /*--------------------------------------------------------------------------
  * Helper functions
  *--------------------------------------------------------------------------
@@ -161,8 +422,7 @@ pgstat_fetch_custom_entry(const char *stat_name)
  * pgstat_create_custom_var_stats
  *		Create new custom statistic entry
  *
- * Initializes a zero-valued statistics entry in shared memory.
- * Validates name length against NAMEDATALEN limit.
+ * Initializes a statistics entry with the given name and description.
  */
 PG_FUNCTION_INFO_V1(pgstat_create_custom_var_stats);
 Datum
@@ -171,6 +431,9 @@ pgstat_create_custom_var_stats(PG_FUNCTION_ARGS)
 	PgStat_EntryRef *entry_ref;
 	PgStatShared_CustomEntry *shared_entry;
 	char	   *stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	   *description = text_to_cstring(PG_GETARG_TEXT_PP(1));
+	dsa_pointer dp = InvalidDsaPointer;
+	bool		found;
 
 	/* Validate name length first */
 	if (strlen(stat_name) >= NAMEDATALEN)
@@ -179,6 +442,20 @@ pgstat_create_custom_var_stats(PG_FUNCTION_ARGS)
 				 errmsg("custom statistic name \"%s\" is too long", stat_name),
 				 errdetail("Name must be less than %d characters.", NAMEDATALEN)));
 
+	/* Initialize DSA and description provided */
+	if (!custom_stats_description_dsa)
+		custom_stats_description_dsa = GetNamedDSA("pgstat_custom_stat_dsa", &found);
+
+	if (!custom_stats_description_dsa)
+		ereport(ERROR,
+				(errmsg("could not access DSA for custom statistics descriptions")));
+
+	/* Allocate space in DSA and copy description */
+	dp = dsa_allocate(custom_stats_description_dsa, strlen(description) + 1);
+	memcpy(dsa_get_address(custom_stats_description_dsa, dp),
+		   description,
+		   strlen(description) + 1);
+
 	/* Create or get existing entry */
 	entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
 											PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), true);
@@ -191,6 +468,9 @@ pgstat_create_custom_var_stats(PG_FUNCTION_ARGS)
 	/* Zero-initialize statistics */
 	memset(&shared_entry->stats, 0, sizeof(shared_entry->stats));
 
+	/* Store description pointer */
+	shared_entry->description = dp;
+
 	pgstat_unlock_entry(entry_ref);
 
 	PG_RETURN_VOID();
@@ -225,8 +505,7 @@ pgstat_update_custom_var_stats(PG_FUNCTION_ARGS)
  * pgstat_drop_custom_var_stats
  *		Remove custom statistic entry
  *
- * Drops the named statistic from shared memory and requests
- * garbage collection if needed.
+ * Drops the named statistic from shared memory.
  */
 PG_FUNCTION_INFO_V1(pgstat_drop_custom_var_stats);
 Datum
@@ -246,7 +525,7 @@ pgstat_drop_custom_var_stats(PG_FUNCTION_ARGS)
  * pgstat_report_custom_var_stats
  *		Retrieve custom statistic values
  *
- * Returns single row with statistic name and call count if the
+ * Returns single row with statistic name, call count, and description if the
  * statistic exists, otherwise returns no rows.
  */
 PG_FUNCTION_INFO_V1(pgstat_report_custom_var_stats);
@@ -280,9 +559,13 @@ pgstat_report_custom_var_stats(PG_FUNCTION_ARGS)
 
 	if (funcctx->call_cntr < funcctx->max_calls)
 	{
-		Datum		values[2];
-		bool		nulls[2] = {false, false};
+		Datum		values[3];
+		bool		nulls[3] = {false, false, false};
 		HeapTuple	tuple;
+		PgStat_EntryRef *entry_ref;
+		PgStatShared_CustomEntry *shared_entry;
+		char	   *description = NULL;
+		bool		found;
 
 		stat_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
 		stat_entry = pgstat_fetch_custom_entry(stat_name);
@@ -290,9 +573,33 @@ pgstat_report_custom_var_stats(PG_FUNCTION_ARGS)
 		/* Return row only if entry exists */
 		if (stat_entry)
 		{
+			/* Get entry ref to access shared entry */
+			entry_ref = pgstat_get_entry_ref(PGSTAT_KIND_TEST_CUSTOM_VAR_STATS, InvalidOid,
+											 PGSTAT_CUSTOM_VAR_STATS_IDX(stat_name), false, NULL);
+
+			if (entry_ref)
+			{
+				shared_entry = (PgStatShared_CustomEntry *) entry_ref->shared_stats;
+
+				/* Get description from DSA if available */
+				if (DsaPointerIsValid(shared_entry->description))
+				{
+					if (!custom_stats_description_dsa)
+						custom_stats_description_dsa = GetNamedDSA("pgstat_custom_stat_dsa", &found);
+
+					if (custom_stats_description_dsa)
+						description = dsa_get_address(custom_stats_description_dsa, shared_entry->description);
+				}
+			}
+
 			values[0] = PointerGetDatum(cstring_to_text(stat_name));
 			values[1] = Int64GetDatum(stat_entry->numcalls);
 
+			if (description)
+				values[2] = PointerGetDatum(cstring_to_text(description));
+			else
+				nulls[2] = true;
+
 			tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
 			SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
 		}
-- 
2.43.0

