Hi All,
While writing a test for shared buffer resizing work, I noticed that
injection_points_attach() with a custom function does not work as
expected when locally attached. See new isolation test
local_custom_injection.spec in the attached patch for a reproducer. In
that test, even if we call injection_points_set_local() before calling
injection_points_attach() with a custom injection point, calling
injection_points_run() from another session still invokes the
injection callback (but should not). This happens because unlike
injection_points_attach(), injection_points_attach_func() does not
construct and pass an InjectionPointCondition object to
InjectionPointAttach(). injection_points_attach() uses the
private_data argument to pass InjectionPointCondition, whereas
injection_points_attach_func() uses private_data to pass the
user-provided attach-time argument.

I think InjectionPointAttach needs two different arguments: one to
pass the InjectionPointCondition() and other to pass user argument at
the time of attach. Attached is a patch to do it. The patch changes
the signature of InjectionPointAttach() and adds code to save the
parameters passed at attach time in look up tables, caches and then
retrieve those when the injection point is run.

While doing so, I noticed that test_aio code didn't notice the change
in the definition of InjectionPointCallback, which led to test_aio
tests failing. The reason is that the callbacks were not declared
using the InjectionPointCallback typedef. The patch now declares
callbacks as `InjectionPointCallback callback_name;` so compilation
fails if function definitions don't match the expected signature. To
enable this, I changed the typedef from a function pointer (`typedef
void (*InjectionPointCallback)(...)`) to a function type (`typedef
void (InjectionPointCallback)(...)`). Irrespective of what happens to
the rest of the patch, I think this change is worth considering for
commit by itself.

Suggestions on new names of the arguments, structure members are welcome.

I have changed the injection_notice() function to print both the
attach parameter and the run-time argument. This has caused a lot of
expected output changes. I think it's better to print (null) when
either of them is not passed, to be clearer. But I am ok if we don't
want to print an argument when one was not passed.

The patch still needs some work as follows:
o. Once we have two separate arguments, the condition_data argument
can be declared as InjectionPointCondition as well as saved in the
InjectionPoint(Cache)Entry as such. I haven't done that right now
since it's a structure local to injection_points.c. We will need to
export it. If we do that, extensions writing a custom injection point
will be able to handle local injection points inside a custom
injection probe. It's not possible to do that now. This limits use of
custom injection points severely, as I faced while writing a test for
shared buffer resizing.

o. Right now non-custom variant injection_point_attach() only takes
two arguments. We could make it to accept three arguments - the third
being the data to be passed at the attach time - just like a custom
variant of the injection_point_attach() function.

o. We depend upon the first byte of the attach parameter being '\0' to
decide whether the user has passed the attach time argument or not. I
think we need a better way to do it; maybe a flag in the
InjectionPoint(Cache)Entry.

o. Given that there are now two arguments one at run time and one at
attach time, the current last argument in InjectionPointCallback
signature should be changed to run_time_arg or some such.

o. I have not updated injection_points_error() and
injection_points_wait() functions to use the attach-time argument. If
we can agree on something, I will do it. For example, when the attach
parameter is passed and its value is true, the respective function
throws an error or waits, otherwise not. When no attach parameter is
passed, the respective function always waits or throws an error. Or if
both the parameters are passed, the respective function waits or
throws error only when both of them are true. I am open to suggestions
on this.

Before proceeding with it, I wanted to check whether the approach looks good.

-- 
Best Wishes,
Ashutosh Bapat
From d37c0d03236f29aa8a695119eb5bb8b6ffd5d3e2 Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Fri, 6 Feb 2026 17:20:36 +0530
Subject: [PATCH v20260206] Custom local injection points

A custom injection point cannot be attached as a local injection point even if
injection_points_set_local() is called before injection_points_attach(). This is
because injection_points_attach_func() does not pass an InjectionPointCondition
object to InjectionPointAttach(). It instead passes the attach-time parameter
passed to injection_points_attach_func(). This is a limitation of custom
injection points.

This patch fixes this limitation by passing two separate arguments to
InjectionPointAttach() - one for the attach-time parameter and the other for the
InjectionPointCondition. These two arguments are saved in the InjectionPoint
entries in the cache and are passed to the callback when the injection point is
hit. As a side change, it declares the injection point callback functions as
InjectionPointCallback to ensure that changes to the signature of
InjectionPointCallback cause compilation failure.

With this change, it's possible to pass attach-time arguments to the built-in
injection points through injection_points_attach(). But that's not done in this
patch.

Author: Ashutosh Bapat <[email protected]>
---
 doc/src/sgml/xfunc.sgml                       |   6 +-
 src/backend/utils/misc/injection_point.c      |  42 ++++---
 src/include/utils/injection_point.h           |  13 ++-
 src/test/modules/injection_points/Makefile    |   1 +
 .../injection_points/expected/hashagg.out     |  40 +++----
 .../expected/injection_points.out             |  32 ++---
 .../expected/local_custom_injection.out       | 109 ++++++++++++++++++
 .../expected/reindex_conc.out                 |   8 +-
 .../injection_points/expected/vacuum.out      |  32 ++---
 .../injection_points/injection_points.c       |  72 +++++++-----
 src/test/modules/injection_points/meson.build |   1 +
 .../specs/local_custom_injection.spec         |  49 ++++++++
 src/test/modules/test_aio/test_aio.c          |  16 +--
 13 files changed, 304 insertions(+), 117 deletions(-)
 create mode 100644 src/test/modules/injection_points/expected/local_custom_injection.out
 create mode 100644 src/test/modules/injection_points/specs/local_custom_injection.spec

diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 70e815b8a2c..c6aa1c3556d 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3862,8 +3862,10 @@ INJECTION_POINT_CACHED(name, arg);
 extern void InjectionPointAttach(const char *name,
                                  const char *library,
                                  const char *function,
-                                 const void *private_data,
-                                 int private_data_size);
+                                 const void *attach_arg,
+                                 int attach_arg_size,
+                                 const void *condition,
+                                 int condition_size);
 </programlisting>
 
      <literal>name</literal> is the name of the injection point, which when
diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c
index c06b0e9b800..6f8a6fb2ebe 100644
--- a/src/backend/utils/misc/injection_point.c
+++ b/src/backend/utils/misc/injection_point.c
@@ -68,7 +68,8 @@ typedef struct InjectionPointEntry
 	 * Opaque data area that modules can use to pass some custom data to
 	 * callbacks, registered when attached.
 	 */
-	char		private_data[INJ_PRIVATE_MAXLEN];
+	char		attach_arg_data[INJ_PRIVATE_MAXLEN];
+	char		condition_data[INJ_PRIVATE_MAXLEN];
 } InjectionPointEntry;
 
 #define MAX_INJECTION_POINTS	128
@@ -95,8 +96,9 @@ NON_EXEC_STATIC InjectionPointsCtl *ActiveInjectionPoints;
 typedef struct InjectionPointCacheEntry
 {
 	char		name[INJ_NAME_MAXLEN];
-	char		private_data[INJ_PRIVATE_MAXLEN];
-	InjectionPointCallback callback;
+	char		attach_arg_data[INJ_PRIVATE_MAXLEN];
+	char		condition_data[INJ_PRIVATE_MAXLEN];
+	InjectionPointCallback *callback;
 
 	/*
 	 * Shmem slot and copy of its generation number when this cache entry was
@@ -118,8 +120,9 @@ static InjectionPointCacheEntry *
 injection_point_cache_add(const char *name,
 						  int slot_idx,
 						  uint64 generation,
-						  InjectionPointCallback callback,
-						  const void *private_data)
+						  InjectionPointCallback *callback,
+						  const void *attach_arg_data,
+						  const void *condition_data)
 {
 	InjectionPointCacheEntry *entry;
 	bool		found;
@@ -147,7 +150,8 @@ injection_point_cache_add(const char *name,
 	entry->slot_idx = slot_idx;
 	entry->generation = generation;
 	entry->callback = callback;
-	memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
+	memcpy(entry->attach_arg_data, attach_arg_data, INJ_PRIVATE_MAXLEN);
+	memcpy(entry->condition_data, condition_data, INJ_PRIVATE_MAXLEN);
 
 	return entry;
 }
@@ -198,7 +202,8 @@ injection_point_cache_load(InjectionPointEntry *entry, int slot_idx, uint64 gene
 									 slot_idx,
 									 generation,
 									 injection_callback_local,
-									 entry->private_data);
+									 entry->attach_arg_data,
+									 entry->condition_data);
 }
 
 /*
@@ -273,8 +278,10 @@ void
 InjectionPointAttach(const char *name,
 					 const char *library,
 					 const char *function,
-					 const void *private_data,
-					 int private_data_size)
+					 const void *attach_arg,
+					 int attach_arg_size,
+					 const void *condition,
+					 int condition_size)
 {
 #ifdef USE_INJECTION_POINTS
 	InjectionPointEntry *entry;
@@ -291,8 +298,11 @@ InjectionPointAttach(const char *name,
 	if (strlen(function) >= INJ_FUNC_MAXLEN)
 		elog(ERROR, "injection point function %s too long (maximum of %u characters)",
 			 function, INJ_FUNC_MAXLEN - 1);
-	if (private_data_size > INJ_PRIVATE_MAXLEN)
-		elog(ERROR, "injection point data too long (maximum of %u bytes)",
+	if (attach_arg_size > INJ_PRIVATE_MAXLEN)
+		elog(ERROR, "injection point attach parameter too long (maximum of %u bytes)",
+			 INJ_PRIVATE_MAXLEN);
+	if (condition_size > INJ_PRIVATE_MAXLEN)
+		elog(ERROR, "injection point condition data too long (maximum of %u bytes)",
 			 INJ_PRIVATE_MAXLEN);
 
 	/*
@@ -333,8 +343,10 @@ InjectionPointAttach(const char *name,
 	strlcpy(entry->name, name, sizeof(entry->name));
 	strlcpy(entry->library, library, sizeof(entry->library));
 	strlcpy(entry->function, function, sizeof(entry->function));
-	if (private_data != NULL)
-		memcpy(entry->private_data, private_data, private_data_size);
+	if (attach_arg != NULL)
+		memcpy(entry->attach_arg_data, attach_arg, attach_arg_size);
+	if (condition != NULL)
+		memcpy(entry->condition_data, condition, condition_size);
 
 	pg_write_barrier();
 	pg_atomic_write_u64(&entry->generation, generation + 1);
@@ -545,7 +557,7 @@ InjectionPointRun(const char *name, void *arg)
 
 	cache_entry = InjectionPointCacheRefresh(name);
 	if (cache_entry)
-		cache_entry->callback(name, cache_entry->private_data, arg);
+		cache_entry->callback(name, cache_entry->attach_arg_data, cache_entry->condition_data, arg);
 #else
 	elog(ERROR, "Injection points are not supported by this build");
 #endif
@@ -562,7 +574,7 @@ InjectionPointCached(const char *name, void *arg)
 
 	cache_entry = injection_point_cache_get(name);
 	if (cache_entry)
-		cache_entry->callback(name, cache_entry->private_data, arg);
+		cache_entry->callback(name, cache_entry->attach_arg_data, cache_entry->condition_data, arg);
 #else
 	elog(ERROR, "Injection points are not supported by this build");
 #endif
diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h
index 27a2526524f..91240a7ae1b 100644
--- a/src/include/utils/injection_point.h
+++ b/src/include/utils/injection_point.h
@@ -42,9 +42,10 @@ typedef struct InjectionPointData
 /*
  * Typedef for callback function launched by an injection point.
  */
-typedef void (*InjectionPointCallback) (const char *name,
-										const void *private_data,
-										void *arg);
+typedef void (InjectionPointCallback) (const char *name,
+									   const void *attach_arg_data,
+									   const void *condition_data,
+									   void *arg);
 
 extern Size InjectionPointShmemSize(void);
 extern void InjectionPointShmemInit(void);
@@ -52,8 +53,10 @@ extern void InjectionPointShmemInit(void);
 extern void InjectionPointAttach(const char *name,
 								 const char *library,
 								 const char *function,
-								 const void *private_data,
-								 int private_data_size);
+								 const void *attach_arg,
+								 int attach_arg_size,
+								 const void *condition,
+								 int condition_size);
 extern void InjectionPointLoad(const char *name);
 extern void InjectionPointRun(const char *name, void *arg);
 extern void InjectionPointCached(const char *name, void *arg);
diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile
index a41d781f8c9..a7a2a997b52 100644
--- a/src/test/modules/injection_points/Makefile
+++ b/src/test/modules/injection_points/Makefile
@@ -14,6 +14,7 @@ REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress
 
 ISOLATION = basic \
 	    inplace \
+	    local_custom_injection \
 	    syscache-update-pruned \
 	    heap_lock_update
 
diff --git a/src/test/modules/injection_points/expected/hashagg.out b/src/test/modules/injection_points/expected/hashagg.out
index cc4247af97d..188c5f4816c 100644
--- a/src/test/modules/injection_points/expected/hashagg.out
+++ b/src/test/modules/injection_points/expected/hashagg.out
@@ -39,26 +39,26 @@ SET max_parallel_workers_per_gather=0;
 SET enable_sort=FALSE;
 SET work_mem='4MB';
 SELECT COUNT(*) FROM (SELECT DISTINCT x FROM hashagg_ij) s;
-NOTICE:  notice triggered for injection point hash-aggregate-spill-1000
-NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode
-NOTICE:  notice triggered for injection point hash-aggregate-single-partition
-NOTICE:  notice triggered for injection point hash-aggregate-process-batch
-NOTICE:  notice triggered for injection point hash-aggregate-spill-1000
-NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode
-NOTICE:  notice triggered for injection point hash-aggregate-single-partition
-NOTICE:  notice triggered for injection point hash-aggregate-process-batch
-NOTICE:  notice triggered for injection point hash-aggregate-spill-1000
-NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode
-NOTICE:  notice triggered for injection point hash-aggregate-single-partition
-NOTICE:  notice triggered for injection point hash-aggregate-process-batch
-NOTICE:  notice triggered for injection point hash-aggregate-spill-1000
-NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode
-NOTICE:  notice triggered for injection point hash-aggregate-single-partition
-NOTICE:  notice triggered for injection point hash-aggregate-process-batch
-NOTICE:  notice triggered for injection point hash-aggregate-spill-1000
-NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode
-NOTICE:  notice triggered for injection point hash-aggregate-single-partition
-NOTICE:  notice triggered for injection point hash-aggregate-process-batch
+NOTICE:  notice triggered for injection point hash-aggregate-spill-1000 (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-single-partition (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-process-batch (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-spill-1000 (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-single-partition (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-process-batch (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-spill-1000 (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-single-partition (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-process-batch (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-spill-1000 (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-single-partition (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-process-batch (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-spill-1000 (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-enter-spill-mode (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-single-partition (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point hash-aggregate-process-batch (attach parameter: (null), run parameter: (null))
  count 
 -------
   5100
diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out
index a3ccaee5472..c049b0ba254 100644
--- a/src/test/modules/injection_points/expected/injection_points.out
+++ b/src/test/modules/injection_points/expected/injection_points.out
@@ -55,28 +55,28 @@ SELECT injection_points_run('TestInjectionBooh'); -- nothing
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog2'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog2
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog2', NULL); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog2
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog2', 'foobar'); -- notice + arg
-NOTICE:  notice triggered for injection point TestInjectionLog2 (foobar)
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: foobar)
  injection_points_run 
 ----------------------
  
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog
+NOTICE:  notice triggered for injection point TestInjectionLog (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -91,14 +91,14 @@ ERROR:  error triggered for injection point TestInjectionError (foobar2)
 -- Re-load cache and run again.
 \c
 SELECT injection_points_run('TestInjectionLog2'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog2
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog
+NOTICE:  notice triggered for injection point TestInjectionLog (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -114,7 +114,7 @@ SELECT injection_points_detach('TestInjectionError'); -- ok
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog
+NOTICE:  notice triggered for injection point TestInjectionLog (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -147,7 +147,7 @@ SELECT injection_points_run('TestInjectionError'); -- nothing
 (1 row)
 
 SELECT injection_points_run('TestInjectionLog2'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog2
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -156,7 +156,7 @@ NOTICE:  notice triggered for injection point TestInjectionLog2
 SELECT injection_points_detach('TestInjectionLog'); -- fails
 ERROR:  could not detach injection point "TestInjectionLog"
 SELECT injection_points_run('TestInjectionLog2'); -- notice
-NOTICE:  notice triggered for injection point TestInjectionLog2
+NOTICE:  notice triggered for injection point TestInjectionLog2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -194,28 +194,28 @@ SELECT injection_points_load('TestInjectionLogLoad'); -- nothing happens
 (1 row)
 
 SELECT injection_points_cached('TestInjectionLogLoad'); -- runs from cache
-NOTICE:  notice triggered for injection point TestInjectionLogLoad
+NOTICE:  notice triggered for injection point TestInjectionLogLoad (attach parameter: (null), run parameter: (null))
  injection_points_cached 
 -------------------------
  
 (1 row)
 
 SELECT injection_points_cached('TestInjectionLogLoad', NULL); -- runs from cache
-NOTICE:  notice triggered for injection point TestInjectionLogLoad
+NOTICE:  notice triggered for injection point TestInjectionLogLoad (attach parameter: (null), run parameter: (null))
  injection_points_cached 
 -------------------------
  
 (1 row)
 
 SELECT injection_points_cached('TestInjectionLogLoad', 'foobar'); -- runs from cache
-NOTICE:  notice triggered for injection point TestInjectionLogLoad (foobar)
+NOTICE:  notice triggered for injection point TestInjectionLogLoad (attach parameter: (null), run parameter: foobar)
  injection_points_cached 
 -------------------------
  
 (1 row)
 
 SELECT injection_points_run('TestInjectionLogLoad'); -- runs from cache
-NOTICE:  notice triggered for injection point TestInjectionLogLoad
+NOTICE:  notice triggered for injection point TestInjectionLogLoad (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -256,7 +256,7 @@ SELECT injection_points_attach('TestConditionLocal2', 'notice');
 SELECT injection_points_run('TestConditionLocal1'); -- error
 ERROR:  error triggered for injection point TestConditionLocal1
 SELECT injection_points_run('TestConditionLocal2'); -- notice
-NOTICE:  notice triggered for injection point TestConditionLocal2
+NOTICE:  notice triggered for injection point TestConditionLocal2 (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
@@ -319,7 +319,7 @@ SELECT injection_points_attach('TestInjectionNoticeFunc', 'injection_points',
 ERROR:  injection point function aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa too long (maximum of 127 characters)
 SELECT injection_points_attach('TestInjectionNoticeFunc', 'injection_points',
   'injection_notice', repeat('a', 1025)::bytea);
-ERROR:  injection point data too long (maximum of 1024 bytes)
+ERROR:  injection point attach parameter too long (maximum of 1024 bytes)
 SELECT injection_points_attach(NULL, NULL, NULL, NULL);
 ERROR:  injection point name cannot be NULL
 SELECT injection_points_attach('TestInjectionNoticeFunc', NULL, NULL, NULL);
@@ -342,7 +342,7 @@ SELECT point_name, library, function FROM injection_points_list()
 (1 row)
 
 SELECT injection_points_run('TestInjectionNoticeFunc', NULL); -- notice
-NOTICE:  notice triggered for injection point TestInjectionNoticeFunc
+NOTICE:  notice triggered for injection point TestInjectionNoticeFunc (attach parameter: (null), run parameter: (null))
  injection_points_run 
 ----------------------
  
diff --git a/src/test/modules/injection_points/expected/local_custom_injection.out b/src/test/modules/injection_points/expected/local_custom_injection.out
new file mode 100644
index 00000000000..2a8b2a3651b
--- /dev/null
+++ b/src/test/modules/injection_points/expected/local_custom_injection.out
@@ -0,0 +1,109 @@
+Parsed test spec with 2 sessions
+
+starting permutation: list1 run1 list2 run2 detach2 detach1
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+step list1: 
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+
+point_name             |library         |function        
+-----------------------+----------------+----------------
+TestInjectionNoticeFunc|injection_points|injection_notice
+(1 row)
+
+s1: NOTICE:  notice triggered for injection point TestInjectionNoticeFunc (attach parameter: attach argument, run parameter: run argument)
+step run1: 
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument');
+
+injection_points_run
+--------------------
+                    
+(1 row)
+
+step list2: 
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+
+point_name             |library         |function        
+-----------------------+----------------+----------------
+TestInjectionNoticeFunc|injection_points|injection_notice
+(1 row)
+
+step run2: 
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument from other backend');
+
+injection_points_run
+--------------------
+                    
+(1 row)
+
+step detach2: 
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
+step detach1: 
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+
+ERROR:  could not detach injection point "TestInjectionNoticeFunc"
+
+starting permutation: list1 run1 list2 run2 detach1 detach2
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+step list1: 
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+
+point_name             |library         |function        
+-----------------------+----------------+----------------
+TestInjectionNoticeFunc|injection_points|injection_notice
+(1 row)
+
+s1: NOTICE:  notice triggered for injection point TestInjectionNoticeFunc (attach parameter: attach argument, run parameter: run argument)
+step run1: 
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument');
+
+injection_points_run
+--------------------
+                    
+(1 row)
+
+step list2: 
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+
+point_name             |library         |function        
+-----------------------+----------------+----------------
+TestInjectionNoticeFunc|injection_points|injection_notice
+(1 row)
+
+step run2: 
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument from other backend');
+
+injection_points_run
+--------------------
+                    
+(1 row)
+
+step detach1: 
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
+step detach2: 
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+
+ERROR:  could not detach injection point "TestInjectionNoticeFunc"
diff --git a/src/test/modules/injection_points/expected/reindex_conc.out b/src/test/modules/injection_points/expected/reindex_conc.out
index db8de4bbe85..263bc95fcb9 100644
--- a/src/test/modules/injection_points/expected/reindex_conc.out
+++ b/src/test/modules/injection_points/expected/reindex_conc.out
@@ -26,13 +26,13 @@ CREATE UNIQUE INDEX ind_expr ON reindex_inj.tbl(ABS(i));
 CREATE UNIQUE INDEX ind_pred ON reindex_inj.tbl(i) WHERE mod(i, 2) = 0;
 CREATE UNIQUE INDEX ind_expr_pred ON reindex_inj.tbl(abs(i)) WHERE mod(i, 2) = 0;
 REINDEX INDEX CONCURRENTLY reindex_inj.ind_simple;
-NOTICE:  notice triggered for injection point reindex-conc-index-safe
+NOTICE:  notice triggered for injection point reindex-conc-index-safe (attach parameter: (null), run parameter: (null))
 REINDEX INDEX CONCURRENTLY reindex_inj.ind_expr;
-NOTICE:  notice triggered for injection point reindex-conc-index-not-safe
+NOTICE:  notice triggered for injection point reindex-conc-index-not-safe (attach parameter: (null), run parameter: (null))
 REINDEX INDEX CONCURRENTLY reindex_inj.ind_pred;
-NOTICE:  notice triggered for injection point reindex-conc-index-not-safe
+NOTICE:  notice triggered for injection point reindex-conc-index-not-safe (attach parameter: (null), run parameter: (null))
 REINDEX INDEX CONCURRENTLY reindex_inj.ind_expr_pred;
-NOTICE:  notice triggered for injection point reindex-conc-index-not-safe
+NOTICE:  notice triggered for injection point reindex-conc-index-not-safe (attach parameter: (null), run parameter: (null))
 -- Cleanup
 SELECT injection_points_detach('reindex-conc-index-safe');
  injection_points_detach 
diff --git a/src/test/modules/injection_points/expected/vacuum.out b/src/test/modules/injection_points/expected/vacuum.out
index 58df59fa927..60f32788d4d 100644
--- a/src/test/modules/injection_points/expected/vacuum.out
+++ b/src/test/modules/injection_points/expected/vacuum.out
@@ -53,14 +53,14 @@ CREATE TABLE vac_tab_off_toast_on(i int, j text) WITH
    vacuum_truncate=false, toast.vacuum_truncate=true);
 -- Multiple relations should use their options in isolation.
 VACUUM vac_tab_on_toast_off, vac_tab_off_toast_on;
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-enabled
-NOTICE:  notice triggered for injection point vacuum-truncate-enabled
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-disabled
-NOTICE:  notice triggered for injection point vacuum-truncate-disabled
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-disabled
-NOTICE:  notice triggered for injection point vacuum-truncate-disabled
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-enabled
-NOTICE:  notice triggered for injection point vacuum-truncate-enabled
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-enabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-enabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-disabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-disabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-disabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-disabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-enabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-enabled (attach parameter: (null), run parameter: (null))
 -- Check "auto" case of index_cleanup and "truncate" controlled by
 -- its GUC.
 CREATE TABLE vac_tab_auto(i int, j text) WITH
@@ -68,16 +68,16 @@ CREATE TABLE vac_tab_auto(i int, j text) WITH
    vacuum_index_cleanup=auto, toast.vacuum_index_cleanup=auto);
 SET vacuum_truncate = false;
 VACUUM vac_tab_auto;
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto
-NOTICE:  notice triggered for injection point vacuum-truncate-disabled
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto
-NOTICE:  notice triggered for injection point vacuum-truncate-disabled
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-disabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-disabled (attach parameter: (null), run parameter: (null))
 SET vacuum_truncate = true;
 VACUUM vac_tab_auto;
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto
-NOTICE:  notice triggered for injection point vacuum-truncate-enabled
-NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto
-NOTICE:  notice triggered for injection point vacuum-truncate-enabled
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-enabled (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-index-cleanup-auto (attach parameter: (null), run parameter: (null))
+NOTICE:  notice triggered for injection point vacuum-truncate-enabled (attach parameter: (null), run parameter: (null))
 RESET vacuum_truncate;
 DROP TABLE vac_tab_auto;
 DROP TABLE vac_tab_on_toast_off;
diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index 3de0491e0ec..674916f23c7 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -93,15 +93,9 @@ typedef struct InjectionPointSharedState
 /* Pointer to shared-memory state. */
 static InjectionPointSharedState *inj_state = NULL;
 
-extern PGDLLEXPORT void injection_error(const char *name,
-										const void *private_data,
-										void *arg);
-extern PGDLLEXPORT void injection_notice(const char *name,
-										 const void *private_data,
-										 void *arg);
-extern PGDLLEXPORT void injection_wait(const char *name,
-									   const void *private_data,
-									   void *arg);
+extern PGDLLEXPORT InjectionPointCallback injection_error;
+extern PGDLLEXPORT InjectionPointCallback injection_notice;
+extern PGDLLEXPORT InjectionPointCallback injection_wait;
 
 /* track if injection points attached in this process are linked to it */
 static bool injection_point_local = false;
@@ -230,9 +224,9 @@ injection_points_cleanup(int code, Datum arg)
 
 /* Set of callbacks available to be attached to an injection point. */
 void
-injection_error(const char *name, const void *private_data, void *arg)
+injection_error(const char *name, const void *attach_arg_data, const void *condition_data, void *arg)
 {
-	const InjectionPointCondition *condition = private_data;
+	const InjectionPointCondition *condition = condition_data;
 	char	   *argstr = arg;
 
 	if (!injection_point_allowed(condition))
@@ -246,29 +240,29 @@ injection_error(const char *name, const void *private_data, void *arg)
 }
 
 void
-injection_notice(const char *name, const void *private_data, void *arg)
+injection_notice(const char *name, const void *attach_arg_data, const void *condition_data, void *arg)
 {
-	const InjectionPointCondition *condition = private_data;
+	const InjectionPointCondition *condition = condition_data;
 	char	   *argstr = arg;
+	const char *attach_arg_data_str = attach_arg_data;
 
 	if (!injection_point_allowed(condition))
 		return;
 
-	if (argstr)
-		elog(NOTICE, "notice triggered for injection point %s (%s)",
-			 name, argstr);
-	else
-		elog(NOTICE, "notice triggered for injection point %s", name);
+	elog(NOTICE, "notice triggered for injection point %s (attach parameter: %s, run parameter: %s)",
+		 name,
+		 attach_arg_data_str[0] != '\0' ? attach_arg_data_str : "(null)",
+		 argstr ? argstr : "(null)");
 }
 
 /* Wait on a condition variable, awaken by injection_points_wakeup() */
 void
-injection_wait(const char *name, const void *private_data, void *arg)
+injection_wait(const char *name, const void *attach_arg_data, const void *condition_data, void *arg)
 {
 	uint32		old_wait_counts = 0;
 	int			index = -1;
 	uint32		injection_wait_event = 0;
-	const InjectionPointCondition *condition = private_data;
+	const InjectionPointCondition *condition = condition_data;
 
 	if (inj_state == NULL)
 		injection_init_shmem();
@@ -352,7 +346,7 @@ injection_points_attach(PG_FUNCTION_ARGS)
 		condition.pid = MyProcPid;
 	}
 
-	InjectionPointAttach(name, "injection_points", function, &condition,
+	InjectionPointAttach(name, "injection_points", function, NULL, 0, &condition,
 						 sizeof(InjectionPointCondition));
 
 	if (injection_point_local)
@@ -379,8 +373,9 @@ injection_points_attach_func(PG_FUNCTION_ARGS)
 	char	   *name;
 	char	   *lib_name;
 	char	   *function;
-	bytea	   *private_data = NULL;
-	int			private_data_size = 0;
+	void	   *attach_arg = NULL;
+	int			attach_arg_size = 0;
+	InjectionPointCondition condition = {0};
 
 	if (PG_ARGISNULL(0))
 		elog(ERROR, "injection point name cannot be NULL");
@@ -395,16 +390,31 @@ injection_points_attach_func(PG_FUNCTION_ARGS)
 
 	if (!PG_ARGISNULL(3))
 	{
-		private_data = PG_GETARG_BYTEA_PP(3);
-		private_data_size = VARSIZE_ANY_EXHDR(private_data);
+		bytea	   *attach_arg_bytea = PG_GETARG_BYTEA_PP(3);
+
+		attach_arg = VARDATA_ANY(attach_arg_bytea);
+		attach_arg_size = VARSIZE_ANY_EXHDR(attach_arg_bytea);
+	}
+
+	if (injection_point_local)
+	{
+		condition.type = INJ_CONDITION_PID;
+		condition.pid = MyProcPid;
+	}
+
+	InjectionPointAttach(name, lib_name, function, attach_arg,
+						 attach_arg_size, &condition, sizeof(condition));
+
+	if (injection_point_local)
+	{
+		MemoryContext oldctx;
+
+		/* Local injection point, so track it for automated cleanup */
+		oldctx = MemoryContextSwitchTo(TopMemoryContext);
+		inj_list_local = lappend(inj_list_local, makeString(pstrdup(name)));
+		MemoryContextSwitchTo(oldctx);
 	}
 
-	if (private_data != NULL)
-		InjectionPointAttach(name, lib_name, function, VARDATA_ANY(private_data),
-							 private_data_size);
-	else
-		InjectionPointAttach(name, lib_name, function, NULL,
-							 0);
 	PG_RETURN_VOID();
 }
 
diff --git a/src/test/modules/injection_points/meson.build b/src/test/modules/injection_points/meson.build
index fcc85414515..a155a1fa6be 100644
--- a/src/test/modules/injection_points/meson.build
+++ b/src/test/modules/injection_points/meson.build
@@ -45,6 +45,7 @@ tests += {
     'specs': [
       'basic',
       'inplace',
+      'local_custom_injection',
       'syscache-update-pruned',
       'heap_lock_update',
     ],
diff --git a/src/test/modules/injection_points/specs/local_custom_injection.spec b/src/test/modules/injection_points/specs/local_custom_injection.spec
new file mode 100644
index 00000000000..0c8aa7c1df4
--- /dev/null
+++ b/src/test/modules/injection_points/specs/local_custom_injection.spec
@@ -0,0 +1,49 @@
+# Test local injection points with custom injection function.
+#
+
+setup
+{
+	CREATE EXTENSION injection_points;
+}
+teardown
+{
+	DROP EXTENSION injection_points;
+}
+
+# Session 1 attaches a local injection point
+session s1
+setup	{
+	SELECT injection_points_set_local();
+	SELECT injection_points_attach('TestInjectionNoticeFunc', 'injection_points',
+	  'injection_notice', 'attach argument');
+}
+step run1	{
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument');
+}
+step list1	{
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+}
+step detach1	{
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+}
+
+# Session 2 tries to run and list the injection point. Since the injection point
+# is local to session 1, it should not be run in this session.
+session s2
+step run2	{
+	SELECT injection_points_run('TestInjectionNoticeFunc', 'run argument from other backend');
+}
+step list2	{
+	SELECT point_name, library, function FROM injection_points_list()
+	  ORDER BY point_name COLLATE "C";
+}
+step detach2	{
+	SELECT injection_points_detach('TestInjectionNoticeFunc');
+}
+
+# Attach in s1, verify s1 can run it and see it in list
+# Then verify s2 can see it in list but running it does nothing
+# Detaching from s2 will succeed which will result in detach from s1 to fail. Reversing the detach sequence, detach from s1 will succeed causing subsequent detach from s2 to fail.
+permutation list1 run1 list2 run2 detach2 detach1
+permutation list1 run1 list2 run2 detach1 detach2
diff --git a/src/test/modules/test_aio/test_aio.c b/src/test/modules/test_aio/test_aio.c
index b1aa8af9ec0..061283d7c05 100644
--- a/src/test/modules/test_aio/test_aio.c
+++ b/src/test/modules/test_aio/test_aio.c
@@ -90,6 +90,8 @@ test_aio_shmem_startup(void)
 							 "test_aio",
 							 "inj_io_short_read",
 							 NULL,
+							 0,
+							 NULL,
 							 0);
 		InjectionPointLoad("aio-process-completion-before-shared");
 
@@ -97,6 +99,8 @@ test_aio_shmem_startup(void)
 							 "test_aio",
 							 "inj_io_reopen",
 							 NULL,
+							 0,
+							 NULL,
 							 0);
 		InjectionPointLoad("aio-worker-after-reopen");
 
@@ -680,15 +684,11 @@ batch_end(PG_FUNCTION_ARGS)
 }
 
 #ifdef USE_INJECTION_POINTS
-extern PGDLLEXPORT void inj_io_short_read(const char *name,
-										  const void *private_data,
-										  void *arg);
-extern PGDLLEXPORT void inj_io_reopen(const char *name,
-									  const void *private_data,
-									  void *arg);
+extern PGDLLEXPORT InjectionPointCallback inj_io_short_read;
+extern PGDLLEXPORT InjectionPointCallback inj_io_reopen;
 
 void
-inj_io_short_read(const char *name, const void *private_data, void *arg)
+inj_io_short_read(const char *name, const void *attach_arg_data, const void *condition_data, void *arg)
 {
 	PgAioHandle *ioh = (PgAioHandle *) arg;
 
@@ -750,7 +750,7 @@ inj_io_short_read(const char *name, const void *private_data, void *arg)
 }
 
 void
-inj_io_reopen(const char *name, const void *private_data, void *arg)
+inj_io_reopen(const char *name, const void *attach_arg_data, const void *condition_data, void *arg)
 {
 	ereport(LOG,
 			errmsg("reopen injection point called, is enabled: %d",

base-commit: 072c8421359730149f4eaf861ce55aa78968ba9d
-- 
2.34.1

Reply via email to