commit efe0f3f5ef3109867382e73410fd802ae2a0e294
Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Date:   Tue Feb 17 16:51:20 2026 +0530

    WIP: resizable shared memory structures

diff --git a/configure.ac b/configure.ac
index 2342780359a..7f2cab9d105 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1895,6 +1895,10 @@ AC_CHECK_DECLS([memset_s], [], [], [#define __STDC_WANT_LIB_EXT1__ 1
 # This is probably only present on macOS, but may as well check always
 AC_CHECK_DECLS(F_FULLFSYNC, [], [], [#include <fcntl.h>])
 
+# Linux-specific madvise constants needed for resizable shared memory. See similar checks in meson.build for explanation of why these checks are here.
+AC_CHECK_DECLS([MADV_POPULATE_WRITE], [], [], [#include <sys/mman.h>])
+AC_CHECK_DECLS([MADV_REMOVE], [], [], [#include <sys/mman.h>])
+
 AC_REPLACE_FUNCS(m4_normalize([
 	explicit_bzero
 	getopt
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 2ebec6928d5..31cd2dabb54 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -4243,8 +4243,39 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
        Size of the allocation in bytes including padding. For anonymous
        allocations, no information about padding is available, so the
        <literal>size</literal> and <literal>allocated_size</literal> columns
-       will always be equal. Padding is not meaningful for free memory, so
-       the columns will be equal in that case also.
+       will always be equal. Padding is not meaningful for free memory, so the
+       columns will be equal in that case also. For resizable allocations which
+       may span multiple memory pages, the padding includes the padding due to
+       page alignment.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>maximum_size</structfield> <type>int8</type>
+      </para>
+      <para>
+       Maximum size in bytes that the allocation can grow upto. For fixed-size allocations
+       <structfield>allocated_size</structfield> and
+       <structfield>maxium_size</structfield> are same. For anonymous
+       allocations, no information about maximum size is available, so the
+       <literal>size</literal> and <literal>maximum_size</literal> columns will
+       always be equal. Maximum size is not meaningful for free memory, so the
+       columns will be equal in that case also.
+      </para></entry>
+     </row>
+
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>allocated_space</structfield> <type>int8</type>
+      </para>
+      <para>
+       Address space, as against the memory, allocated for this allocation in
+       terms of bytes for resizable structures. It is greater than or equal to
+       <structfield>allocated_size</structfield> for these structures. It also
+       includes padding, if any. For fixed-size allocations, anonymous
+       allocations, and free memory this is same as
+       <structfield>allocated_size</structfield>.
       </para></entry>
      </row>
     </tbody>
diff --git a/meson.build b/meson.build
index 8b134b28a69..5310a9789aa 100644
--- a/meson.build
+++ b/meson.build
@@ -2837,6 +2837,22 @@ decl_checks = [
   ['timingsafe_bcmp',  'string.h'],
 ]
 
+# Linux-specific madvise constants needed for resizable shared memory.
+# Usually we use AC_CHECK_DECLS to check for function declarations, but in this
+# case we are using it to detect existence of constants. These constants are
+# used to define HAVE_RESIZABLE_SHMEM which is used in storage/pg_shmem.h as
+# well as storage/shmem.h. The first abstracts the APIs to allocate shared
+# memory segments from the operating system whereas the second abstracts APIs to
+# allocate shared memory to various subsystems. Since they are related but
+# orthogonal to each other, including any one of them in the other file doesn't
+# make sense. pg_config_manual.h is the only place where HAVE_RESIZABLE_SHMEM
+# can be defined and made available to both without including sys/mman.h. But
+# for that we need constants that indicate the existence of following defines.
+decl_checks += [
+  ['MADV_POPULATE_WRITE', 'sys/mman.h'],
+  ['MADV_REMOVE', 'sys/mman.h'],
+]
+
 # Need to check for function declarations for these functions, because
 # checking for library symbols wouldn't handle deployment target
 # restrictions on macOS
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 2e3886cf9fe..2e71a6823f0 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -589,6 +589,27 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 	return true;
 }
 
+/*
+ * Get the page size being used by the shared memory.
+ *
+ * The function should be called only after the shared memory has been setup.
+ */
+Size
+GetOSPageSize(void)
+{
+	Size		os_page_size;
+
+	Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
+
+	os_page_size = sysconf(_SC_PAGESIZE);
+
+	/* If huge pages are actually in use, use huge page size */
+	if (huge_pages_status == HUGE_PAGES_ON)
+		GetHugePageSize(&os_page_size, NULL);
+
+	return os_page_size;
+}
+
 /*
  * Creates an anonymous mmap()ed shared memory segment.
  *
@@ -991,3 +1012,53 @@ PGSharedMemoryDetach(void)
 		AnonymousShmem = NULL;
 	}
 }
+
+#ifdef HAVE_RESIZABLE_SHMEM
+/*
+ * Make sure that the memory of given size from the given address is released.
+ *
+ * The address and size are expected to be page aligned.
+ *
+ * Only supported on platforms that support anonymous shared memory.
+ */
+void
+PGSharedMemoryEnsureFreed(void *addr, Size size)
+{
+	if (!AnonymousShmem)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("only anonymous shared memory can be freed")));
+
+	Assert(addr == (void *) TYPEALIGN(GetOSPageSize(), addr));
+	Assert(size == TYPEALIGN(GetOSPageSize(), size));
+	Assert(size > 0);
+
+	if (madvise(addr, size, MADV_REMOVE) == -1)
+		ereport(ERROR,
+				(errmsg("could not free shared memory: %m")));
+}
+
+/*
+ * Make sure that the memory of given size from the given address is allocated.
+ *
+ * The address and size are expected to be page aligned.
+ *
+ * Only supported on platforms that support anonymous shared memory.
+ */
+void
+PGSharedMemoryEnsureAllocated(void *addr, Size size)
+{
+	if (!AnonymousShmem)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("only anonymous shared memory can be allocated at runtime")));
+
+	Assert(addr == (void *) TYPEALIGN(GetOSPageSize(), addr));
+	Assert(size == TYPEALIGN(GetOSPageSize(), size));
+	Assert(size > 0);
+
+	if (madvise(addr, size, MADV_POPULATE_WRITE) == -1)
+		ereport(ERROR,
+				(errmsg("could not allocate shared memory: %m")));
+}
+#endif							/* HAVE_RESIZABLE_SHMEM */
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index 794e4fcb2ad..dc2ee018845 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -648,3 +648,26 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
 	}
 	return true;
 }
+
+/*
+ * Get the page size used by the shared memory.
+ *
+ * The function should be called only after the shared memory has been setup.
+ */
+Size
+GetOSPageSize(void)
+{
+	SYSTEM_INFO sysinfo;
+	Size		os_page_size;
+
+	Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
+
+	GetSystemInfo(&sysinfo);
+	os_page_size = sysinfo.dwPageSize;
+
+	/* If huge pages are actually in use, use huge page size */
+	if (huge_pages_status == HUGE_PAGES_ON)
+		GetHugePageSize(&os_page_size, NULL);
+
+	return os_page_size;
+}
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index dbb6684ac10..0c974ce1125 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -170,6 +170,13 @@ typedef struct
 	ShmemAreaKind kind;
 } ShmemRequest;
 
+#ifdef HAVE_RESIZABLE_SHMEM
+#define SHMEM_REQUEST_SPACE_SIZE(request) \
+	((request)->options->maximum_size > 0 ? (request)->options->maximum_size : (request)->options->size)
+#else
+#define SHMEM_REQUEST_SPACE_SIZE(request) ((request)->options->size)
+#endif
+
 static List *pending_shmem_requests;
 
 /*
@@ -271,6 +278,10 @@ typedef struct
 	void	   *location;		/* location in shared mem */
 	Size		size;			/* # bytes requested for the structure */
 	Size		allocated_size; /* # bytes actually allocated */
+#ifdef HAVE_RESIZABLE_SHMEM
+	Size		maximum_size;	/* the maximum size a structure can grow to */
+	Size		allocated_space;	/* the total address space allocated */
+#endif
 } ShmemIndexEnt;
 
 /* To get reliable results for NUMA inquiry we need to "touch pages" once */
@@ -278,6 +289,9 @@ static bool firstNumaTouch = true;
 
 static bool AttachOrInitShmemIndexEntry(ShmemRequest *request,
 										bool may_init, bool may_attach);
+#ifdef HAVE_RESIZABLE_SHMEM
+static Size EstimateAllocatedSize(ShmemIndexEnt *entry);
+#endif
 
 Datum		pg_numa_available(PG_FUNCTION_ARGS);
 
@@ -349,6 +363,11 @@ ShmemRequestInternal(ShmemStructDesc *desc, ShmemStructOpts *options,
 		if (options->size <= 0 && options->size != SHMEM_ATTACH_UNKNOWN_SIZE)
 			elog(ERROR, "invalid size %zd for shared memory request for \"%s\"",
 				 options->size, options->name);
+#ifdef HAVE_RESIZABLE_SHMEM
+		if (options->maximum_size < 0 && options->maximum_size != SHMEM_ATTACH_UNKNOWN_SIZE)
+			elog(ERROR, "invalid maximum_size %zd for shared memory request for \"%s\"",
+				 options->maximum_size, options->name);
+#endif
 	}
 	else
 	{
@@ -357,8 +376,24 @@ ShmemRequestInternal(ShmemStructDesc *desc, ShmemStructOpts *options,
 		if (options->size <= 0)
 			elog(ERROR, "invalid size %zd for shared memory request for \"%s\"",
 				 options->size, options->name);
+#ifdef HAVE_RESIZABLE_SHMEM
+		if (options->maximum_size == SHMEM_ATTACH_UNKNOWN_SIZE)
+			elog(ERROR, "SHMEM_ATTACH_UNKNOWN_SIZE cannot be used during startup");
+		if (options->maximum_size < 0)
+			elog(ERROR, "invalid maximum_size %zd for shared memory request for \"%s\"",
+				 options->maximum_size, options->name);
+#endif
 	}
 
+#ifdef HAVE_RESIZABLE_SHMEM
+	if (options->maximum_size > 0 && options->size >= options->maximum_size)
+		elog(ERROR, "resizable shared memory structure \"%s\" should have maximum size (%zd) greater than size (%zd)",
+			 options->name, options->maximum_size, options->size);
+
+	if (options->maximum_size > 0 && shared_memory_type != SHMEM_TYPE_MMAP)
+		elog(ERROR, "resizable shared memory requires shared_memory_type = mmap");
+#endif
+
 	if (shmem_request_state != SRS_REQUESTING)
 		elog(ERROR, "ShmemRequestStruct can only be called from a shmem_request callback");
 
@@ -379,8 +414,12 @@ ShmemRequestInternal(ShmemStructDesc *desc, ShmemStructOpts *options,
 }
 
 /*
- *	ShmemGetRequestedSize() --- estimate the total size of all registered shared
- *                              memory structures.
+ * ShmemGetRequestedSize() --- estimate the total size of all registered shared
+ * memory structures.
+ *
+ * When maximum_size is specified for a request, we use that instead of the
+ * initial size for the estimation, to ensure that enough memory is reserved for
+ * resizable structures.
  *
  * This is called once at postmaster startup, before the shared memory segment
  * has been created.
@@ -397,7 +436,7 @@ ShmemGetRequestedSize(void)
 	/* memory needed for all the requested areas */
 	foreach_ptr(ShmemRequest, request, pending_shmem_requests)
 	{
-		size = add_size(size, request->options->size);
+		size = add_size(size, SHMEM_REQUEST_SPACE_SIZE(request));
 		size = add_size(size, request->options->extra_size);
 		size = add_size(size, request->options->alignment);
 	}
@@ -583,13 +622,18 @@ AttachOrInitShmemIndexEntry(ShmemRequest *request,
 	{
 		/*
 		 * We inserted the entry to the shared memory index. Allocate
-		 * requested amount of shared memory for it, and do basic
-		 * initializion.
+		 * requested amount of address space in the shared memory segment for
+		 * it, and do basic initializion. The memory gets mapped during
+		 * initialization as the corresponding memory pages are written to.
+		 * Allocate enough space for a resizable structure to grow to its
+		 * maximum size. It is expected that the initialization callback will
+		 * use only as much memory as the initial size of the resizable
+		 * structure.
 		 */
 		size_t		allocated_size;
 		void	   *structPtr;
 
-		structPtr = ShmemAllocRaw(request->options->size, request->options->alignment, &allocated_size);
+		structPtr = ShmemAllocRaw(SHMEM_REQUEST_SPACE_SIZE(request), request->options->alignment, &allocated_size);
 		if (structPtr == NULL)
 		{
 			/* out of memory; remove the failed ShmemIndex entry */
@@ -601,11 +645,29 @@ AttachOrInitShmemIndexEntry(ShmemRequest *request,
 							desc->name, request->options->size)));
 		}
 		index_entry->size = request->options->size;
+#ifdef HAVE_RESIZABLE_SHMEM
+		index_entry->maximum_size = SHMEM_REQUEST_SPACE_SIZE(request);
+		index_entry->allocated_space = allocated_size;
+		if (request->options->maximum_size > 0)
+		{
+			/* Resizable structure. */
+			index_entry->allocated_size = EstimateAllocatedSize(index_entry);
+		}
+		else
+		{
+			/* Fixed-size structure. */
+			index_entry->allocated_size = allocated_size;
+		}
+#else
 		index_entry->allocated_size = allocated_size;
+#endif
 		index_entry->location = structPtr;
 
 		desc->ptr = index_entry->location;
 		desc->size = index_entry->size;
+#ifdef HAVE_RESIZABLE_SHMEM
+		desc->maximum_size = index_entry->maximum_size;
+#endif
 
 		/*
 		 * Re-establish the caller's pointer variable, or do other actions to
@@ -629,6 +691,102 @@ AttachOrInitShmemIndexEntry(ShmemRequest *request,
 	return found;
 }
 
+#ifdef HAVE_RESIZABLE_SHMEM
+/*
+ * Estimate the actual memory allocated for a resizable structure.
+ */
+static Size
+EstimateAllocatedSize(ShmemIndexEnt *entry)
+{
+	Size		page_size = GetOSPageSize();
+	char	   *align_end = (char *) TYPEALIGN(page_size, (char *) entry->location + entry->size);
+	char	   *floor_max_end = (char *) TYPEALIGN_DOWN(page_size, (char *) entry->location + entry->maximum_size);
+
+	Assert(entry->maximum_size >= entry->size);
+	Assert(entry->allocated_space >= entry->maximum_size);
+
+	if (align_end >= floor_max_end)
+	{
+		/*
+		 * A resizable structure which ends on the same page irrespective of
+		 * its size. The structure will be allocated maximum memory at the
+		 * beginning.
+		 */
+		return entry->allocated_space;
+	}
+	else
+	{
+		/*
+		 * The maximal structure spans multiple pages. At the beginning the
+		 * pages between the page where this structure, with its initial size,
+		 * ends and the page where the next structure starts will not be
+		 * allocated.
+		 */
+		return entry->allocated_space - (floor_max_end - align_end);
+	}
+}
+
+void
+ShmemResizeRegistered(const ShmemStructDesc *desc, Size new_size)
+{
+	ShmemIndexEnt *result;
+	bool		found;
+	Size		page_size = GetOSPageSize();
+	char	   *new_end;
+
+	Assert(new_size > 0);
+
+	if (desc->maximum_size <= 0)
+		elog(ERROR, "shared memory struct \"%s\" is not resizable", desc->name);
+
+	/* look it up in the shmem index */
+	LWLockAcquire(ShmemIndexLock, LW_EXCLUSIVE);
+	result = (ShmemIndexEnt *) hash_search(ShmemIndex, desc->name, HASH_FIND, &found);
+	if (!found)
+		elog(ERROR, "shmem struct \"%s\" is not initialized", desc->name);
+
+	Assert(result);
+
+	if (result->maximum_size != desc->maximum_size)
+		elog(ERROR, "shmem struct \"%s\" has corrupted descriptor", desc->name);
+
+	if (result->maximum_size < new_size)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
+				 errmsg("not enough address space is reserved for resizing structure \"%s\"", desc->name)));
+
+	/*
+	 * When shrinking the memory from the page aligned new end to the start of
+	 * the page containing end of the reserved space is not required. Whereas
+	 * when expanding the memory from the start of the page containing the
+	 * start of the structure to the page aligned new end is required.
+	 */
+	new_end = (char *) TYPEALIGN(page_size, (char *) result->location + new_size);
+	if (new_size < result->size)
+	{
+		char	   *max_end = (char *) TYPEALIGN_DOWN(page_size, (char *) result->location + result->maximum_size);
+		Size		free_size = max_end - new_end;
+
+		if (free_size > 0)
+			PGSharedMemoryEnsureFreed(new_end, free_size);
+	}
+	else if (new_size > result->size)
+	{
+		char	   *struct_start = (char *) TYPEALIGN_DOWN(page_size, (char *) result->location);
+		Size		alloc_size = new_end - struct_start;
+
+		if (alloc_size > 0)
+			PGSharedMemoryEnsureAllocated(struct_start, alloc_size);
+	}
+
+	/* Update shmem index entry. */
+	result->size = new_size;
+	result->allocated_size = EstimateAllocatedSize(result);
+
+	LWLockRelease(ShmemIndexLock);
+}
+#endif							/* HAVE_RESIZABLE_SHMEM */
+
 /*
  *	InitShmemAllocator() --- set up basic pointers to shared memory.
  *
@@ -737,6 +895,10 @@ InitShmemAllocator(PGShmemHeader *seghdr)
 		Assert(!found);
 		result->size = size;
 		result->allocated_size = size;
+#ifdef HAVE_RESIZABLE_SHMEM
+		result->maximum_size = size;
+		result->allocated_space = size;
+#endif
 		result->location = ShmemAllocator->index;
 	}
 }
@@ -1028,7 +1190,7 @@ mul_size(Size s1, Size s2)
 Datum
 pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 {
-#define PG_GET_SHMEM_SIZES_COLS 4
+#define PG_GET_SHMEM_SIZES_COLS 6
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	HASH_SEQ_STATUS hstat;
 	ShmemIndexEnt *ent;
@@ -1050,7 +1212,23 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 		values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr);
 		values[2] = Int64GetDatum(ent->size);
 		values[3] = Int64GetDatum(ent->allocated_size);
+#ifdef HAVE_RESIZABLE_SHMEM
+		values[4] = Int64GetDatum(ent->maximum_size);
+		values[5] = Int64GetDatum(ent->allocated_space);
+
+		/*
+		 * Keep track of the total allocated space for named shmem areas, to
+		 * be able to calculate the amount of shared memory allocated for
+		 * anonymous areas and the amount of free shared memory at the end of
+		 * the segment.
+		 */
+		named_allocated += ent->allocated_space;
+#else
+		values[4] = Int64GetDatum(ent->size);
+		values[5] = Int64GetDatum(ent->allocated_size);
+
 		named_allocated += ent->allocated_size;
+#endif
 
 		tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
 							 values, nulls);
@@ -1061,6 +1239,8 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	nulls[1] = true;
 	values[2] = Int64GetDatum(ShmemAllocator->free_offset - named_allocated);
 	values[3] = values[2];
+	values[4] = values[2];
+	values[5] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	/* output as-of-yet unused shared memory */
@@ -1069,6 +1249,8 @@ pg_get_shmem_allocations(PG_FUNCTION_ARGS)
 	nulls[1] = false;
 	values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemAllocator->free_offset);
 	values[3] = values[2];
+	values[4] = values[2];
+	values[5] = values[2];
 	tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
 
 	LWLockRelease(ShmemIndexLock);
@@ -1256,23 +1438,9 @@ pg_get_shmem_allocations_numa(PG_FUNCTION_ARGS)
 Size
 pg_get_shmem_pagesize(void)
 {
-	Size		os_page_size;
-#ifdef WIN32
-	SYSTEM_INFO sysinfo;
-
-	GetSystemInfo(&sysinfo);
-	os_page_size = sysinfo.dwPageSize;
-#else
-	os_page_size = sysconf(_SC_PAGESIZE);
-#endif
-
 	Assert(IsUnderPostmaster);
-	Assert(huge_pages_status != HUGE_PAGES_UNKNOWN);
-
-	if (huge_pages_status == HUGE_PAGES_ON)
-		GetHugePageSize(&os_page_size, NULL);
 
-	return os_page_size;
+	return GetOSPageSize();
 }
 
 Datum
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 0a862693fcd..0c5eb36d9e6 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1210,6 +1210,13 @@
   max => '1000.0',
 },
 
+{ name => 'have_resizable_shmem', type => 'bool', context => 'PGC_INTERNAL', group => 'PRESET_OPTIONS',
+  short_desc => 'Shows whether the running server supports resizable shared memory.',
+  flags => 'GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE',
+  variable => 'have_resizable_shmem_enabled',
+  boot_val => 'HAVE_RESIZABLE_SHMEM_ENABLED',
+},
+
 { name => 'hba_file', type => 'string', context => 'PGC_POSTMASTER', group => 'FILE_LOCATIONS',
   short_desc => 'Sets the server\'s "hba" configuration file.',
   flags => 'GUC_SUPERUSER_ONLY',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1e14b7b4af0..91db51c18b1 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -646,6 +646,13 @@ static bool assert_enabled = DEFAULT_ASSERT_ENABLED;
 #endif
 static bool exec_backend_enabled = EXEC_BACKEND_ENABLED;
 
+#ifdef HAVE_RESIZABLE_SHMEM
+#define HAVE_RESIZABLE_SHMEM_ENABLED true
+#else
+#define HAVE_RESIZABLE_SHMEM_ENABLED false
+#endif
+static bool have_resizable_shmem_enabled = HAVE_RESIZABLE_SHMEM_ENABLED;
+
 static char *recovery_target_timeline_string;
 static char *recovery_target_string;
 static char *recovery_target_xid_string;
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3579cec5744..75d380256af 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8664,8 +8664,8 @@
 { oid => '5052', descr => 'allocations from the main shared memory segment',
   proname => 'pg_get_shmem_allocations', prorows => '50', proretset => 't',
   provolatile => 'v', prorettype => 'record', proargtypes => '',
-  proallargtypes => '{text,int8,int8,int8}', proargmodes => '{o,o,o,o}',
-  proargnames => '{name,off,size,allocated_size}',
+  proallargtypes => '{text,int8,int8,int8,int8,int8}', proargmodes => '{o,o,o,o,o,o}',
+  proargnames => '{name,off,size,allocated_size,maximum_size,allocated_space}',
   prosrc => 'pg_get_shmem_allocations',
   proacl => '{POSTGRES=X,pg_read_all_stats=X}' },
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d8d61918aff..554b5ab78d7 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -85,6 +85,14 @@
    don't. */
 #undef HAVE_DECL_F_FULLFSYNC
 
+/* Define to 1 if you have the declaration of `MADV_POPULATE_WRITE', and to 0
+   if you don't. */
+#undef HAVE_DECL_MADV_POPULATE_WRITE
+
+/* Define to 1 if you have the declaration of `MADV_REMOVE', and to 0 if you
+   don't. */
+#undef HAVE_DECL_MADV_REMOVE
+
 /* Define to 1 if you have the declaration of `memset_s', and to 0 if you
    don't. */
 #undef HAVE_DECL_MEMSET_S
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index 521b49b8888..78d2b7a63c4 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -131,6 +131,15 @@
 #define EXEC_BACKEND
 #endif
 
+/*
+ * HAVE_RESIZABLE_SHMEM indicates whether  resizable shared memory structures are
+ * supported. The implementation requires anonymous mmap and Linux-specific
+ * madvise constants (MADV_REMOVE and MADV_POPULATE_WRITE).
+ */
+#if HAVE_DECL_MADV_REMOVE && HAVE_DECL_MADV_POPULATE_WRITE && !defined(EXEC_BACKEND)
+#define HAVE_RESIZABLE_SHMEM
+#endif
+
 /*
  * USE_POSIX_FADVISE controls whether Postgres will attempt to use the
  * posix_fadvise() kernel call.  Usually the automatic configure tests are
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 10c7b065861..3d5aceba59c 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -89,6 +89,11 @@ extern PGShmemHeader *PGSharedMemoryCreate(Size size,
 										   PGShmemHeader **shim);
 extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
 extern void PGSharedMemoryDetach(void);
+#ifdef HAVE_RESIZABLE_SHMEM
+extern void PGSharedMemoryEnsureFreed(void *addr, Size size);
+extern void PGSharedMemoryEnsureAllocated(void *addr, Size size);
+#endif
 extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
+extern Size GetOSPageSize(void);
 
 #endif							/* PG_SHMEM_H */
diff --git a/src/include/storage/shmem.h b/src/include/storage/shmem.h
index 4939130aab1..5e25de2e7c3 100644
--- a/src/include/storage/shmem.h
+++ b/src/include/storage/shmem.h
@@ -43,6 +43,9 @@ typedef struct ShmemStructDesc
 
 	void	   *ptr;
 	size_t		size;
+#ifdef HAVE_RESIZABLE_SHMEM
+	size_t		maximum_size;
+#endif
 } ShmemStructDesc;
 
 #define SHMEM_ATTACH_UNKNOWN_SIZE (-1)
@@ -76,6 +79,18 @@ typedef struct ShmemStructOpts
 	 */
 	size_t		extra_size;
 
+#ifdef HAVE_RESIZABLE_SHMEM
+
+	/*
+	 * Maximum size this structure can grow upto in future. The memory is not
+	 * allocated right away but the corresponding address space is reserved so
+	 * that memory can be mapped to it when the structure grows. Typically
+	 * should be used for large resizable structures which need contiguous
+	 * memory.
+	 */
+	size_t		maximum_size;
+#endif
+
 	/*
 	 * When the shmem area is initialized or attached to, pointer to it is
 	 * stored in *ptr.  It usually points to a global variable, used to access
@@ -220,6 +235,9 @@ extern void *ShmemHashAlloc(Size size, void *alloc_arg);
 extern bool ShmemAddrIsValid(const void *addr);
 
 extern void RegisterShmemCallbacks(const ShmemCallbacks *callbacks);
+#ifdef HAVE_RESIZABLE_SHMEM
+extern void ShmemResizeRegistered(const ShmemStructDesc *desc, Size new_size);
+#endif
 
 extern void ShmemRequestInternal(ShmemStructDesc *desc, ShmemStructOpts *options,
 								 ShmemAreaKind kind);
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 62fab9f3c2f..3ef8228851a 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -14,6 +14,7 @@ SUBDIRS = \
 		  libpq_pipeline \
 		  oauth_validator \
 		  plsample \
+		  resizable_shmem \
 		  spgist_name_ops \
 		  test_aio \
 		  test_binaryheap \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 6799ba11e11..5244b4bdb8f 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -13,6 +13,7 @@ subdir('libpq_pipeline')
 subdir('nbtree')
 subdir('oauth_validator')
 subdir('plsample')
+subdir('resizable_shmem')
 subdir('spgist_name_ops')
 subdir('ssl_passphrase_callback')
 subdir('test_aio')
diff --git a/src/test/modules/resizable_shmem/Makefile b/src/test/modules/resizable_shmem/Makefile
new file mode 100644
index 00000000000..f3bd8ac0c7f
--- /dev/null
+++ b/src/test/modules/resizable_shmem/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/resizable_shmem/Makefile
+
+MODULES = resizable_shmem
+TAP_TESTS = 1
+
+EXTENSION = resizable_shmem
+DATA = resizable_shmem--1.0.sql
+PGFILEDESC = "resizable_shmem - test module for resizable shared memory"
+
+# This test requires library to be loaded at the server start, so disable
+# installcheck
+NO_INSTALLCHECK = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/resizable_shmem
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/src/makefiles/pgxs.mk
+endif
diff --git a/src/test/modules/resizable_shmem/meson.build b/src/test/modules/resizable_shmem/meson.build
new file mode 100644
index 00000000000..493bbbc95c3
--- /dev/null
+++ b/src/test/modules/resizable_shmem/meson.build
@@ -0,0 +1,36 @@
+# src/test/modules/resizable_shmem/meson.build
+
+resizable_shmem_sources = files(
+  'resizable_shmem.c',
+)
+
+if host_system == 'windows'
+  resizable_shmem_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'resizable_shmem',
+    '--FILEDESC', 'resizable_shmem - test module for resizable shared memory',])
+endif
+
+resizable_shmem = shared_module('resizable_shmem',
+  resizable_shmem_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += resizable_shmem
+
+test_install_data += files(
+  'resizable_shmem.control',
+  'resizable_shmem--1.0.sql',
+)
+
+tests += {
+  'name': 'resizable_shmem',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_resizable_shmem.pl',
+    ],
+    # This test requires library to be loaded at the server start, so disable
+    # installcheck
+    'runningcheck': false,
+  },
+}
diff --git a/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql b/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql
new file mode 100644
index 00000000000..c1bcb6117b6
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem--1.0.sql
@@ -0,0 +1,37 @@
+/* src/test/modules/resizable_shmem/resizable_shmem--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION resizable_shmem" to load this file. \quit
+
+-- Function to resize the test structure in the shared memory
+CREATE FUNCTION resizable_shmem_resize(new_entries integer)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to write data to all entries in the test structure in shared memory
+-- Writing all the entries makes sure that the memory is actually allocated and
+-- mapped to the process, so that we can later measure the memory usage.
+CREATE FUNCTION resizable_shmem_write(entry_value integer)
+RETURNS void
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to verify that specified number of initial entries have expected value.
+-- Reading all the entries makes sure that the memory is actually mapped to the
+-- process, so that we can later measure the memory usage.
+CREATE FUNCTION resizable_shmem_read(entry_count integer, entry_value integer)
+RETURNS boolean
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to report memory usage statistics of the calling backend
+CREATE FUNCTION resizable_shmem_usage(OUT rss_anon bigint, OUT rss_file bigint, OUT rss_shmem bigint, OUT vm_size bigint)
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
+
+-- Function to get the shared memory page size
+CREATE FUNCTION resizable_shmem_pagesize()
+RETURNS integer
+AS 'MODULE_PATHNAME'
+LANGUAGE C STRICT;
diff --git a/src/test/modules/resizable_shmem/resizable_shmem.c b/src/test/modules/resizable_shmem/resizable_shmem.c
new file mode 100644
index 00000000000..2657150531f
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem.c
@@ -0,0 +1,281 @@
+/* -------------------------------------------------------------------------
+ *
+ * resizable_shmem.c
+ *		Test module for PostgreSQL's resizable shared memory functionality
+ *
+ * This module demonstrates and tests the resizable shared memory API
+ * provided by shmem.c/shmem.h.
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "storage/shmem.h"
+#include "storage/spin.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+#include "utils/timestamp.h"
+#include "access/htup_details.h"
+
+#include <stdio.h>
+
+PG_MODULE_MAGIC;
+
+/*
+ * Default amount of shared buffers and hence the amount of shared memory
+ * allocated by default is in hundreds of MBs. The memory allocated to the test
+ * structure will be noticeable only when it's in the same order.
+ */
+#define TEST_INITIAL_ENTRIES	(25 * 1024 * 1024)	/* Initial number of
+													 * entries (100MB) */
+#define TEST_MAX_ENTRIES		(100 * 1024 * 1024) /* Maximum number of
+													 * entries (400MB, 4x
+													 * initial) */
+#define TEST_ENTRY_SIZE			sizeof(int32)	/* Size of each entry */
+
+/*
+ * Resizable test data structure stored in shared memory.
+ *
+ * We do not use any locks. The test performs resizing, reads and writes none of
+ * which are concurrent to keep the code and the test simple.
+ */
+typedef struct TestResizableShmemStruct
+{
+	/* Metadata */
+	int32		num_entries;	/* Number of entries that can fit */
+
+	/* Data area - variable size */
+	int32		data[FLEXIBLE_ARRAY_MEMBER];
+} TestResizableShmemStruct;
+
+static ShmemStructDesc testShmemDesc;
+
+/* Global pointer to our shared memory structure */
+static TestResizableShmemStruct *resizable_shmem = NULL;
+
+static void resizable_shmem_request(void *arg);
+static void resizable_shmem_shmem_init(void *arg);
+
+static const ShmemCallbacks pgss_shmem_callbacks = {
+	.request_fn = resizable_shmem_request,
+	.init_fn = resizable_shmem_shmem_init,
+};
+
+/* SQL-callable functions */
+PG_FUNCTION_INFO_V1(resizable_shmem_resize);
+PG_FUNCTION_INFO_V1(resizable_shmem_write);
+PG_FUNCTION_INFO_V1(resizable_shmem_read);
+PG_FUNCTION_INFO_V1(resizable_shmem_usage);
+PG_FUNCTION_INFO_V1(resizable_shmem_pagesize);
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+	/*
+	 * The module needs to be loaded via shared_preload_libraries to register
+	 * shared memory structure. But if that's not the case, don't throw an
+	 * error. The SQL functions check for existence of the shared memory data
+	 * structure.
+	 */
+	if (!process_shared_preload_libraries_in_progress)
+		return;
+
+	RegisterShmemCallbacks(&pgss_shmem_callbacks);
+}
+
+/*
+ * Request shared memory resources
+ */
+static void
+resizable_shmem_request(void *arg)
+{
+	/* Register our resizable shared memory structure */
+	ShmemRequestStruct(&testShmemDesc,
+					   .name = "resizable_shmem",
+					   .size = offsetof(TestResizableShmemStruct, data) + (TEST_INITIAL_ENTRIES * TEST_ENTRY_SIZE),
+#ifdef HAVE_RESIZABLE_SHMEM
+					   .maximum_size = offsetof(TestResizableShmemStruct, data) + (TEST_MAX_ENTRIES * TEST_ENTRY_SIZE),
+#endif
+					   .ptr = (void **) &resizable_shmem,
+		);
+}
+
+/*
+ * Initialize shared memory structure
+ */
+static void
+resizable_shmem_shmem_init(void *arg)
+{
+	/*
+	 * Shared memory structure should have been allocated with the requested
+	 * size. Initialize the metadata.
+	 */
+	Assert(resizable_shmem != NULL);
+	Assert(testShmemDesc.size >= offsetof(TestResizableShmemStruct, data) + (TEST_INITIAL_ENTRIES * TEST_ENTRY_SIZE));
+#ifdef HAVE_RESIZABLE_SHMEM
+	Assert(testShmemDesc.maximum_size >= offsetof(TestResizableShmemStruct, data) + (TEST_MAX_ENTRIES * TEST_ENTRY_SIZE));
+#endif
+
+	resizable_shmem->num_entries = TEST_INITIAL_ENTRIES;
+	memset(resizable_shmem->data, 0, TEST_INITIAL_ENTRIES * TEST_ENTRY_SIZE);
+}
+
+/*
+ * Resize the shared memory structure to accommodate the specified number of
+ * entries.
+ */
+Datum
+resizable_shmem_resize(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_RESIZABLE_SHMEM
+	int32		new_entries = PG_GETARG_INT32(0);
+	Size		new_size;
+
+	if (!resizable_shmem)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("resizable_shmem is not initialized")));
+
+	new_size = offsetof(TestResizableShmemStruct, data) + (new_entries * TEST_ENTRY_SIZE);
+	ShmemResizeRegistered(&testShmemDesc, new_size);
+	resizable_shmem->num_entries = new_entries;
+
+	PG_RETURN_VOID();
+#else
+	ereport(ERROR,
+			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+			 errmsg("resizable shared memory is not supported on this platform")));
+#endif
+}
+
+/*
+ * Write the given integer value to all entries in the data array.
+ */
+Datum
+resizable_shmem_write(PG_FUNCTION_ARGS)
+{
+	int32		entry_value = PG_GETARG_INT32(0);
+	int32		i;
+
+	if (!resizable_shmem)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("resizable_shmem is not initialized")));
+
+	/* Write the value to all current entries */
+	for (i = 0; i < resizable_shmem->num_entries; i++)
+		resizable_shmem->data[i] = entry_value;
+
+	PG_RETURN_VOID();
+}
+
+/*
+ * Check whether the first 'entry_count' entries all have the expected 'entry_value'.
+ * Returns true if all match, false otherwise.
+ */
+Datum
+resizable_shmem_read(PG_FUNCTION_ARGS)
+{
+	int32		entry_count = PG_GETARG_INT32(0);
+	int32		entry_value = PG_GETARG_INT32(1);
+	int32		i;
+
+	if (resizable_shmem == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("resizable_shmem is not initialized")));
+
+	/* Validate entry_count */
+	if (entry_count < 0 || entry_count > resizable_shmem->num_entries)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("entry_count %d is out of range (0..%d)", entry_count, resizable_shmem->num_entries)));
+
+	/* Check if first entry_count entries have the expected value */
+	for (i = 0; i < entry_count; i++)
+	{
+		if (resizable_shmem->data[i] != entry_value)
+			PG_RETURN_BOOL(false);
+	}
+
+	PG_RETURN_BOOL(true);
+}
+
+/*
+ * Report multiple memory usage statistics of the calling backend process
+ * as reported by the kernel.
+ * Returns RssAnon, RssFile, RssShmem, VmSize from /proc/self/status as a record.
+ *
+ * TODO: See TODO note in SQL definition of this function.
+ */
+Datum
+resizable_shmem_usage(PG_FUNCTION_ARGS)
+{
+	FILE	   *f;
+	char		line[256];
+	int64		rss_anon_kb = -1;
+	int64		rss_file_kb = -1;
+	int64		rss_shmem_kb = -1;
+	int64		vm_size_kb = -1;
+	int			found = 0;
+	TupleDesc	tupdesc;
+	Datum		values[4];
+	bool		nulls[4];
+	HeapTuple	tuple;
+
+	/* Open /proc/self/status to read memory information */
+	f = fopen("/proc/self/status", "r");
+	if (f == NULL)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open /proc/self/status: %m")));
+
+	/* Look for the memory usage lines */
+	while (fgets(line, sizeof(line), f) != NULL && found < 4)
+	{
+		if (rss_anon_kb == -1 && sscanf(line, "RssAnon: %ld kB", &rss_anon_kb) == 1)
+			found++;
+		else if (rss_file_kb == -1 && sscanf(line, "RssFile: %ld kB", &rss_file_kb) == 1)
+			found++;
+		else if (rss_shmem_kb == -1 && sscanf(line, "RssShmem: %ld kB", &rss_shmem_kb) == 1)
+			found++;
+		else if (vm_size_kb == -1 && sscanf(line, "VmSize: %ld kB", &vm_size_kb) == 1)
+			found++;
+	}
+
+	fclose(f);
+
+	/* Build tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("function returning record called in context "
+						"that cannot accept a record")));
+
+	/* Build the result tuple */
+	values[0] = Int64GetDatum(rss_anon_kb >= 0 ? rss_anon_kb * 1024 : 0);
+	values[1] = Int64GetDatum(rss_file_kb >= 0 ? rss_file_kb * 1024 : 0);
+	values[2] = Int64GetDatum(rss_shmem_kb >= 0 ? rss_shmem_kb * 1024 : 0);
+	values[3] = Int64GetDatum(vm_size_kb >= 0 ? vm_size_kb * 1024 : 0);
+
+	nulls[0] = nulls[1] = nulls[2] = nulls[3] = false;
+
+	tuple = heap_form_tuple(tupdesc, values, nulls);
+	PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
+}
+
+/*
+ * resizable_shmem_pagesize() - Get the shared memory page size
+ */
+Datum
+resizable_shmem_pagesize(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_INT32(pg_get_shmem_pagesize());
+}
diff --git a/src/test/modules/resizable_shmem/resizable_shmem.control b/src/test/modules/resizable_shmem/resizable_shmem.control
new file mode 100644
index 00000000000..1ce2c5ea21a
--- /dev/null
+++ b/src/test/modules/resizable_shmem/resizable_shmem.control
@@ -0,0 +1,5 @@
+# resizable_shmem extension test module
+comment = 'test module for testing resizable shared memory structure functionality'
+default_version = '1.0'
+module_pathname = '$libdir/resizable_shmem'
+relocatable = true
diff --git a/src/test/modules/resizable_shmem/t/001_resizable_shmem.pl b/src/test/modules/resizable_shmem/t/001_resizable_shmem.pl
new file mode 100644
index 00000000000..c08b1e66c65
--- /dev/null
+++ b/src/test/modules/resizable_shmem/t/001_resizable_shmem.pl
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Test resizable shared memory functionality
+
+my $node = PostgreSQL::Test::Cluster->new('resizable_shmem');
+
+$node->init;
+# Need to configure for resizable_shmem when the server starts
+$node->append_conf('postgresql.conf', 'shared_preload_libraries = resizable_shmem');
+# Reduce the shared memory usage as much as possible so that resizable_shmem
+# structure dominates the shared memory usage and it's easy to detect any
+# digressions in the expected shared memory usage because of this structure.
+$node->append_conf('postgresql.conf', 'shared_buffers = 128kB');
+$node->append_conf('postgresql.conf', 'max_connections = 5');
+$node->append_conf('postgresql.conf', 'max_worker_processes = 0');
+$node->append_conf('postgresql.conf', 'max_wal_senders = 0');
+$node->append_conf('postgresql.conf', 'max_prepared_transactions = 0');
+$node->append_conf('postgresql.conf', 'max_locks_per_transaction = 10');
+$node->append_conf('postgresql.conf', 'max_pred_locks_per_transaction = 10');
+$node->append_conf('postgresql.conf', 'wal_buffers = 32kB');
+$node->start;
+
+# Create extension
+$node->safe_psql('postgres', 'CREATE EXTENSION resizable_shmem;');
+
+# Detect whether resizable shared memory is supported on this platform
+my $have_resizable_shmem =
+  $node->safe_psql('postgres', 'SHOW have_resizable_shmem;') eq 'on';
+
+my $num_entries = 25 * 1024 * 1024; # Initial number of entries in resizable shared memory
+my $max_entries = 100 * 1024 * 1024; # Maximum number of entries allowed
+
+# Basic read/write should work on all platforms
+my $value = 100;
+$node->safe_psql('postgres', "SELECT resizable_shmem_write($value);");
+is($node->safe_psql('postgres', "SELECT resizable_shmem_read($num_entries, $value);"),
+   't', 'data read after write successful');
+
+if ($have_resizable_shmem)
+{
+	# Query string variables for reuse
+	my $total_alloc_query = "SELECT sum(allocated_size) FROM pg_shmem_allocations;";
+	# Currently only one structure is resizable
+	my $fixed_struct_query = "SELECT count(*) FROM pg_shmem_allocations WHERE name <> 'resizable_shmem' and size <> maximum_size;";
+
+	my $page_size = $node->safe_psql('postgres', "SELECT resizable_shmem_pagesize();");
+
+	# Create background sessions for testing
+	my $session1 = $node->background_psql('postgres');
+	my $session2 = $node->background_psql('postgres');
+
+	# Verify that RssShmem does not exceed the total allocated shared memory.
+	# Allocated shared memory should be mostly the memory allocated to the
+	# resizable_shmem structure. Any large increase in expected RssShmem
+	# reflects the unepxected increase in memory allocated to the
+	# resizable_shmem structure.
+	sub check_shmem_usage
+	{
+		my ($session, $label) = @_;
+
+		my $rss_shmem = $session->query_safe('SELECT rss_shmem FROM resizable_shmem_usage();', verbose => 0);
+		my $total_alloc = $node->safe_psql('postgres', $total_alloc_query);
+
+		note "$label: RssShmem=$rss_shmem, sum(allocated_size)=$total_alloc";
+		ok($rss_shmem <= $total_alloc, "$label: RssShmem does not exceed total allocated size");
+	}
+
+	$value = 100;
+	# Write and read the initial set of entries.
+	$session1->query_safe("SELECT resizable_shmem_write($value);", verbose => 0);
+	is($session2->query_safe("SELECT resizable_shmem_read($num_entries, $value);", verbose => 0), 't', 'data read after write successful');
+	check_shmem_usage($session1, 'initial write (session 1)');
+	check_shmem_usage($session2, 'initial write (session 2)');
+	is($node->safe_psql('postgres', $fixed_struct_query), '0', 'initial fixed sized structures');
+
+	# Helper to test a resize operation: resize, verify old data, write new
+	# data, verify new data, check shmem usage and fixed-size structures.
+	sub test_resize
+	{
+		my ($new_num_entries, $new_value, $resize_session, $label) = @_;
+
+		my $old_num_entries = $num_entries;
+		$num_entries = $new_num_entries;
+
+		$resize_session->query_safe("SELECT resizable_shmem_resize($num_entries);", verbose => 0);
+
+		# Old data should still be intact in the (possibly smaller) area
+		my $readable_entries = ($num_entries < $old_num_entries) ? $num_entries : $old_num_entries;
+		is($session1->query_safe("SELECT resizable_shmem_read($readable_entries, $value);", verbose => 0), 't', "old data readable after $label");
+
+		$value = $new_value;
+		$session2->query_safe("SELECT resizable_shmem_write($value);", verbose => 0);
+		is($session1->query_safe("SELECT resizable_shmem_read($num_entries, $value);", verbose => 0), 't', "new data readable after $label");
+
+		check_shmem_usage($session1, "$label (session 1)");
+		check_shmem_usage($session2, "$label (session 2)");
+		is($node->safe_psql('postgres', $fixed_struct_query), '0', "fixed sized structures after $label");
+		note $node->safe_psql('postgres', "SELECT * FROM pg_shmem_allocations WHERE name <> 'resizable_shmem' and size <> maximum_size;");
+	}
+
+	# Resize to maximum
+	test_resize($max_entries, 500, $session1, 'resize to maximum');
+
+	# Shrink to smaller size
+	test_resize(75 * 1024 * 1024, 999, $session2, 'shrinking');
+
+	# Resize to the same size (no-op)
+	test_resize($num_entries, 1999, $session2, 'no-op resize');
+
+	# Test resize failure (attempt to resize beyond max - should fail)
+	my ($ret, $stdout, $stderr) = $node->psql('postgres', "SELECT resizable_shmem_resize(" . ($max_entries * 2) . ");");
+	ok($ret != 0 || $stderr =~ /ERROR/, 'Resize beyond maximum fails');
+
+	# Cleanup sessions
+	$session1->quit;
+	$session2->quit;
+}
+else
+{
+	# On unsupported platforms, resizing should fail with a clear error
+	my ($ret, $stdout, $stderr) = $node->psql('postgres', "SELECT resizable_shmem_resize($num_entries);");
+	ok($ret != 0, 'resize fails on unsupported platform');
+	like($stderr, qr/not supported/, 'resize error mentions not supported');
+}
+
+# Cleanup
+$node->stop;
+
+done_testing();
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 2b3cf6d8569..24b5ba58ad1 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1770,8 +1770,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename,
 pg_shmem_allocations| SELECT name,
     off,
     size,
-    allocated_size
-   FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size);
+    allocated_size,
+    maximum_size,
+    allocated_space
+   FROM pg_get_shmem_allocations() pg_get_shmem_allocations(name, off, size, allocated_size, maximum_size, allocated_space);
 pg_shmem_allocations_numa| SELECT name,
     numa_node,
     size
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 8563d6d2c97..d110240e2eb 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3137,6 +3137,7 @@ TestDSMRegistryHashEntry
 TestDSMRegistryStruct
 TestDecodingData
 TestDecodingTxnData
+TestResizableShmemStruct
 TestShmemData
 TestSpec
 TestValueType
