From 82472c69efe4c7d8d07023eb4ef3b08bb33b97f2 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 25 Aug 2026 19:12:09 +0300
Subject: [PATCH 3/3] Add tests for failing shmem allocations after startup

To cover the failure cases that the previous commits hardened.

Author: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Discussion: https://www.postgresql.org/message-id/CAJTYsWVRRWH48=PcuAo_2Y4Ap6M0QRmzxgUfFkNRtdWK74LjBQ@mail.gmail.com
Backpatch-through: 19
---
 src/test/modules/test_shmem/Makefile          |  3 ++
 src/test/modules/test_shmem/meson.build       |  3 ++
 .../test_shmem/t/001_late_shmem_alloc.pl      | 47 ++++++++++++++++++-
 src/test/modules/test_shmem/test_shmem.c      | 26 +++++++++-
 4 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/src/test/modules/test_shmem/Makefile b/src/test/modules/test_shmem/Makefile
index 2407f7462fe..fed8e29c8f5 100644
--- a/src/test/modules/test_shmem/Makefile
+++ b/src/test/modules/test_shmem/Makefile
@@ -2,6 +2,9 @@
 
 PGFILEDESC = "test_shmem - test code for shmem allocations"
 
+EXTRA_INSTALL = src/test/modules/injection_points
+export enable_injection_points
+
 MODULE_big = test_shmem
 OBJS = \
 	$(WIN32RES) \
diff --git a/src/test/modules/test_shmem/meson.build b/src/test/modules/test_shmem/meson.build
index fb4bf328b8f..8f98f2c4e31 100644
--- a/src/test/modules/test_shmem/meson.build
+++ b/src/test/modules/test_shmem/meson.build
@@ -26,6 +26,9 @@ tests += {
   'sd': meson.current_source_dir(),
   'bd': meson.current_build_dir(),
   'tap': {
+    'env': {
+      'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+    },
     'tests': [
       't/001_late_shmem_alloc.pl',
     ],
diff --git a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
index 5cf07d071ec..b09889f4fb0 100644
--- a/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
+++ b/src/test/modules/test_shmem/t/001_late_shmem_alloc.pl
@@ -15,8 +15,50 @@ my $node = PostgreSQL::Test::Cluster->new('main');
 $node->init;
 $node->start;
 
+# Test a failure in initialization of the shared memory area.
+SKIP:
+{
+	skip "injection points not supported by this build",
+	  if $ENV{enable_injection_points} ne 'yes';
+	$node->safe_psql("postgres", "CREATE EXTENSION injection_points;");
+	$node->safe_psql("postgres",
+		"SELECT injection_points_attach('test-shmem-init', 'error');");
+
+	# A failure in the requesting shared memory should not affect server
+	# availability. The session should remain useful, however trying to create
+	# the same extension again will fail.
+	my (undef, undef, $stderr) =
+	  $node->psql("postgres", "CREATE EXTENSION test_shmem;");
+	like($stderr,
+		qr/error triggered for injection point test-shmem-init/,
+		"failure in initialization is reported");
+	$node->safe_psql("postgres",
+		"SELECT injection_points_detach('test-shmem-init');");
+	(undef, undef, $stderr) =
+	  $node->psql("postgres", "CREATE EXTENSION test_shmem;");
+	like($stderr, qr/cannot attach to shared memory/,
+		"post-init extension creation fails");
+
+	# Only a server restart can remove the partially allocated shared memory
+	# area.
+	$node->restart;
+}
+
+# Test failure when the request is larger than the memory reserved for
+# after-startup requests.
+my $session = $node->background_psql('postgres', on_error_stop => 0);
+$session->query(q[SET test_shmem.area_size = '128kB';], verbose => 0);
+$session->query("CREATE EXTENSION test_shmem;", verbose => 0);
+like($session->{stderr}, qr/not enough shared memory/,
+	"an after-startup request larger than the reserve fails");
 
-$node->safe_psql("postgres", "CREATE EXTENSION test_shmem;");
+# The server and the backend should still be available. Since there was only one
+# area requested, the failure did not change anything in the shared memory.
+# Verify that the request for smaller area succeeds in the same session.
+$session->{stderr} = '';
+$session->query("SET test_shmem.area_size = default;", verbose => 0);
+$session->query_safe("CREATE EXTENSION test_shmem;", verbose => 0);
+$session->quit;
 
 # Check that the attach counter is incremented on a new connection
 my $attach_count1 =
@@ -28,8 +70,9 @@ cmp_ok($attach_count2, '>', $attach_count1,
 $node->stop;
 
 ###
-# Test that loading via shared_preload_libraries also works
+# Test that loading via shared_preload_libraries also works, even for large request.
 ###
+$node->append_conf('postgresql.conf', "test_shmem.area_size = '128kB'");
 $node->append_conf('postgresql.conf',
 	"shared_preload_libraries = 'test_shmem'");
 $node->start;
diff --git a/src/test/modules/test_shmem/test_shmem.c b/src/test/modules/test_shmem/test_shmem.c
index 9bd4012b435..2956f488135 100644
--- a/src/test/modules/test_shmem/test_shmem.c
+++ b/src/test/modules/test_shmem/test_shmem.c
@@ -17,9 +17,13 @@
 
 #include "postgres.h"
 
+#include <limits.h>
+
 #include "fmgr.h"
 #include "miscadmin.h"
 #include "storage/shmem.h"
+#include "utils/guc.h"
+#include "utils/injection_point.h"
 
 
 PG_MODULE_MAGIC;
@@ -29,11 +33,14 @@ typedef struct TestShmemData
 	int			value;
 	bool		initialized;
 	int			attach_count;
+	char		variable_sized_array[FLEXIBLE_ARRAY_MEMBER];
 } TestShmemData;
 
 static TestShmemData *TestShmem;
 
 static bool attached_or_initialized = false;
+static int	test_shmem_area_size = sizeof(TestShmemData);
+static bool test_shmem_guc_defined = false;
 
 static void test_shmem_request(void *arg);
 static void test_shmem_init(void *arg);
@@ -52,7 +59,7 @@ test_shmem_request(void *arg)
 	elog(LOG, "test_shmem_request callback called");
 
 	ShmemRequestStruct(.name = "test_shmem area",
-					   .size = sizeof(TestShmemData),
+					   .size = test_shmem_area_size,
 					   .ptr = (void **) &TestShmem);
 }
 
@@ -60,6 +67,8 @@ static void
 test_shmem_init(void *arg)
 {
 	elog(LOG, "init callback called");
+	/* Induce an error while initializing shared structure. */
+	INJECTION_POINT("test-shmem-init", NULL);
 	if (TestShmem->initialized)
 		elog(ERROR, "shmem area already initialized");
 	TestShmem->initialized = true;
@@ -86,6 +95,21 @@ void
 _PG_init(void)
 {
 	elog(LOG, "test_shmem module's _PG_init called");
+
+	if (!test_shmem_guc_defined)
+	{
+		DefineCustomIntVariable("test_shmem.area_size",
+								"Size of the shmem area to request.",
+								NULL,
+								&test_shmem_area_size,
+								sizeof(TestShmemData),
+								sizeof(TestShmemData), INT_MAX,
+								PGC_USERSET,
+								GUC_UNIT_BYTE,
+								NULL, NULL, NULL);
+		MarkGUCPrefixReserved("test_shmem");
+		test_shmem_guc_defined = true;
+	}
 	RegisterShmemCallbacks(&TestShmemCallbacks);
 }
 
-- 
2.47.3

