Hi all,
(Andres in CC.)

While reading the AIO code I have noticed that the way it uses
injection points is limited by the fact that we don't have support for
runtime parameters in the existing injection point facility.  The code
is shaped with two set/get functions that set a static parameter that
the injection callback would reuse internally, using
pgaio_inj_io_get(), and pgaio_io_call_inj() and a static
pgaio_inj_cur_handle.  Relying on a static variable for that is not a
good idea, IMO, even if the stack is reset with a TRY/CATCH block on
error in the callback run.

Supporting runtime parameters in injection points is something that I
have mentioned as wanted a couple of times, but I was waiting for an
actual use-case in core before adding support for it, as mentioned
around here:
https://www.postgresql.org/message-id/z_yn_galw-pe2...@paquier.xyz

test_aio is bringing one.

We are still in early April, and I'd like to propose a cleanup of the
AIO code on HEAD, even if we are post-feature freeze, to not release
this new code in this state, implying an open item.  Please note that
I'm OK to take responsibility for this patch set at the end, reviews
are welcome.

Anyway, I have spent some time with my mind on that, and finished
with the attached patch set:
- 0001 is the addition of runtime parameters in the backend code.  I
have made the choice of extending the existing INJECTION_POINT() and
INJECTION_POINT_CACHED() instead of introducing new macros.  That's a
matter of taste, perhaps, but increasing the number of these macros
leads to a confusing result.  This one is more invasive, but that's OK
for me.  The code is shaped so as we can rely on
InjectionPointCallback to define the shape of a callback.  It is 
possible to pass down a full structure if one wants, that the callback
is then responsible for translating back.  The AIO code only want an
AIO handle, which is simple.
- 0002 introduces a few updates to the module injection_points, adding
support for runtime parameters and some tests.
- 0003 cleans up the AOI test code, relying on 0001.

The CI is passing.  Thoughts and comments are welcome.
--
Michael
From 7e8453987d127144477a8e7e9fa7ae61e48091be Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Mon, 14 Apr 2025 16:24:18 +0900
Subject: [PATCH 1/3] Add support for runtime arguments in injection points

The macros INJECTION_POINT() and INJECTION_POINT_CACHED() are extended
with an optional argument that can be passed down to the callback
defined.
---
 src/include/utils/injection_point.h               | 15 ++++++++-------
 src/backend/access/gin/ginbtree.c                 |  6 +++---
 src/backend/access/heap/heapam.c                  |  2 +-
 src/backend/access/index/genam.c                  |  2 +-
 src/backend/access/transam/multixact.c            |  4 ++--
 src/backend/access/transam/xlog.c                 |  2 +-
 src/backend/commands/indexcmds.c                  |  4 ++--
 src/backend/executor/nodeAgg.c                    | 10 +++++-----
 src/backend/libpq/be-secure-gssapi.c              |  2 +-
 src/backend/libpq/be-secure.c                     |  2 +-
 src/backend/postmaster/autovacuum.c               |  2 +-
 src/backend/storage/aio/aio.c                     |  2 +-
 src/backend/tcop/backend_startup.c                |  2 +-
 src/backend/tcop/postgres.c                       |  6 +++---
 src/backend/utils/cache/catcache.c                |  2 +-
 src/backend/utils/cache/inval.c                   |  2 +-
 src/backend/utils/cache/typcache.c                |  2 +-
 src/backend/utils/init/postinit.c                 |  2 +-
 src/backend/utils/misc/injection_point.c          |  8 ++++----
 .../modules/injection_points/injection_points.c   |  4 ++--
 src/test/modules/test_slru/test_multixact.c       |  2 +-
 doc/src/sgml/xfunc.sgml                           | 11 +++++++----
 22 files changed, 49 insertions(+), 45 deletions(-)

diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h
index 6ba64cd1ebc6..470e1fc582d8 100644
--- a/src/include/utils/injection_point.h
+++ b/src/include/utils/injection_point.h
@@ -16,13 +16,13 @@
  */
 #ifdef USE_INJECTION_POINTS
 #define INJECTION_POINT_LOAD(name) InjectionPointLoad(name)
-#define INJECTION_POINT(name) InjectionPointRun(name)
-#define INJECTION_POINT_CACHED(name) InjectionPointCached(name)
+#define INJECTION_POINT(name, arg) InjectionPointRun(name, arg)
+#define INJECTION_POINT_CACHED(name, arg) InjectionPointCached(name, arg)
 #define IS_INJECTION_POINT_ATTACHED(name) IsInjectionPointAttached(name)
 #else
 #define INJECTION_POINT_LOAD(name) ((void) name)
-#define INJECTION_POINT(name) ((void) name)
-#define INJECTION_POINT_CACHED(name) ((void) name)
+#define INJECTION_POINT(name, arg) ((void) name)
+#define INJECTION_POINT_CACHED(name, arg) ((void) name)
 #define IS_INJECTION_POINT_ATTACHED(name) (false)
 #endif
 
@@ -30,7 +30,8 @@
  * Typedef for callback function launched by an injection point.
  */
 typedef void (*InjectionPointCallback) (const char *name,
-										const void *private_data);
+										const void *private_data,
+										const void *arg);
 
 extern Size InjectionPointShmemSize(void);
 extern void InjectionPointShmemInit(void);
@@ -41,8 +42,8 @@ extern void InjectionPointAttach(const char *name,
 								 const void *private_data,
 								 int private_data_size);
 extern void InjectionPointLoad(const char *name);
-extern void InjectionPointRun(const char *name);
-extern void InjectionPointCached(const char *name);
+extern void InjectionPointRun(const char *name, const void *arg);
+extern void InjectionPointCached(const char *name, const void *arg);
 extern bool IsInjectionPointAttached(const char *name);
 extern bool InjectionPointDetach(const char *name);
 
diff --git a/src/backend/access/gin/ginbtree.c b/src/backend/access/gin/ginbtree.c
index 26a0bdc2063a..644d484ea53c 100644
--- a/src/backend/access/gin/ginbtree.c
+++ b/src/backend/access/gin/ginbtree.c
@@ -685,9 +685,9 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
 
 #ifdef USE_INJECTION_POINTS
 		if (GinPageIsLeaf(BufferGetPage(stack->buffer)))
-			INJECTION_POINT("gin-leave-leaf-split-incomplete");
+			INJECTION_POINT("gin-leave-leaf-split-incomplete", NULL);
 		else
-			INJECTION_POINT("gin-leave-internal-split-incomplete");
+			INJECTION_POINT("gin-leave-internal-split-incomplete", NULL);
 #endif
 
 		/* search parent to lock */
@@ -778,7 +778,7 @@ ginFinishSplit(GinBtree btree, GinBtreeStack *stack, bool freestack,
 static void
 ginFinishOldSplit(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats, int access)
 {
-	INJECTION_POINT("gin-finish-incomplete-split");
+	INJECTION_POINT("gin-finish-incomplete-split", NULL);
 	elog(DEBUG1, "finishing incomplete split of block %u in gin index \"%s\"",
 		 stack->blkno, RelationGetRelationName(btree->index));
 
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index ed2e30217992..b01e82ea9712 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -3304,7 +3304,7 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
 	interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
 
 	block = ItemPointerGetBlockNumber(otid);
-	INJECTION_POINT("heap_update-before-pin");
+	INJECTION_POINT("heap_update-before-pin", NULL);
 	buffer = ReadBuffer(relation, block);
 	page = BufferGetPage(buffer);
 
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index 8f532e14590e..0cb27af13109 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -851,7 +851,7 @@ systable_inplace_update_begin(Relation relation,
 		if (retries++ > 10000)
 			elog(ERROR, "giving up after too many tries to overwrite row");
 
-		INJECTION_POINT("inplace-before-pin");
+		INJECTION_POINT("inplace-before-pin", NULL);
 		scan = systable_beginscan(relation, indexId, indexOK, snapshot,
 								  nkeys, unconstify(ScanKeyData *, key));
 		oldtup = systable_getnext(scan);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 9d25a7df0d32..7a827df354f8 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -872,7 +872,7 @@ MultiXactIdCreateFromMembers(int nmembers, MultiXactMember *members)
 	 */
 	multi = GetNewMultiXactId(nmembers, &offset);
 
-	INJECTION_POINT_CACHED("multixact-create-from-members");
+	INJECTION_POINT_CACHED("multixact-create-from-members", NULL);
 
 	/* Make an XLOG entry describing the new MXID. */
 	xlrec.mid = multi;
@@ -1486,7 +1486,7 @@ retry:
 			LWLockRelease(lock);
 			CHECK_FOR_INTERRUPTS();
 
-			INJECTION_POINT("multixact-get-members-cv-sleep");
+			INJECTION_POINT("multixact-get-members-cv-sleep", NULL);
 
 			ConditionVariableSleep(&MultiXactState->nextoff_cv,
 								   WAIT_EVENT_MULTIXACT_CREATION);
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index ec40c0b7c42b..207f68259301 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7882,7 +7882,7 @@ CreateRestartPoint(int flags)
 	 * This location needs to be after CheckPointGuts() to ensure that some
 	 * work has already happened during this checkpoint.
 	 */
-	INJECTION_POINT("create-restart-point");
+	INJECTION_POINT("create-restart-point", NULL);
 
 	/*
 	 * Remember the prior checkpoint's redo ptr for
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 33c2106c17ce..d962fe392cd2 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -3892,9 +3892,9 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 
 #ifdef USE_INJECTION_POINTS
 		if (idx->safe)
-			INJECTION_POINT("reindex-conc-index-safe");
+			INJECTION_POINT("reindex-conc-index-safe", NULL);
 		else
-			INJECTION_POINT("reindex-conc-index-not-safe");
+			INJECTION_POINT("reindex-conc-index-not-safe", NULL);
 #endif
 
 		idx->tableId = RelationGetRelid(heapRel);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index f83fc16c5c8e..377e016d7322 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1492,7 +1492,7 @@ build_hash_tables(AggState *aggstate)
 		if (IS_INJECTION_POINT_ATTACHED("hash-aggregate-oversize-table"))
 		{
 			nbuckets = memory / TupleHashEntrySize();
-			INJECTION_POINT_CACHED("hash-aggregate-oversize-table");
+			INJECTION_POINT_CACHED("hash-aggregate-oversize-table", NULL);
 		}
 #endif
 
@@ -1882,7 +1882,7 @@ hash_agg_check_limits(AggState *aggstate)
 		if (IS_INJECTION_POINT_ATTACHED("hash-aggregate-spill-1000"))
 		{
 			do_spill = true;
-			INJECTION_POINT_CACHED("hash-aggregate-spill-1000");
+			INJECTION_POINT_CACHED("hash-aggregate-spill-1000", NULL);
 		}
 	}
 #endif
@@ -1910,7 +1910,7 @@ hash_agg_check_limits(AggState *aggstate)
 static void
 hash_agg_enter_spill_mode(AggState *aggstate)
 {
-	INJECTION_POINT("hash-aggregate-enter-spill-mode");
+	INJECTION_POINT("hash-aggregate-enter-spill-mode", NULL);
 	aggstate->hash_spill_mode = true;
 	hashagg_recompile_expressions(aggstate, aggstate->table_filled, true);
 
@@ -2739,7 +2739,7 @@ agg_refill_hash_table(AggState *aggstate)
 	 */
 	hashagg_recompile_expressions(aggstate, true, true);
 
-	INJECTION_POINT("hash-aggregate-process-batch");
+	INJECTION_POINT("hash-aggregate-process-batch", NULL);
 	for (;;)
 	{
 		TupleTableSlot *spillslot = aggstate->hash_spill_rslot;
@@ -2995,7 +2995,7 @@ hashagg_spill_init(HashAggSpill *spill, LogicalTapeSet *tapeset, int used_bits,
 	{
 		npartitions = 1;
 		partition_bits = 0;
-		INJECTION_POINT_CACHED("hash-aggregate-single-partition");
+		INJECTION_POINT_CACHED("hash-aggregate-single-partition", NULL);
 	}
 #endif
 
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index a3d610b1373d..717ba9824f91 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -500,7 +500,7 @@ secure_open_gssapi(Port *port)
 				minor;
 	gss_cred_id_t delegated_creds;
 
-	INJECTION_POINT("backend-gssapi-startup");
+	INJECTION_POINT("backend-gssapi-startup", NULL);
 
 	/*
 	 * Allocate subsidiary Port data for GSSAPI operations.
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 91576f942857..d723e74e8137 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -131,7 +131,7 @@ secure_open_server(Port *port)
 	}
 	Assert(pq_buffer_remaining_data() == 0);
 
-	INJECTION_POINT("backend-ssl-startup");
+	INJECTION_POINT("backend-ssl-startup", NULL);
 
 	r = be_tls_open_server(port);
 
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 16756152b71a..4d4a1a3197ec 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1922,7 +1922,7 @@ do_autovacuum(void)
 	 * This injection point is put in a transaction block to work with a wait
 	 * that uses a condition variable.
 	 */
-	INJECTION_POINT("autovacuum-worker-start");
+	INJECTION_POINT("autovacuum-worker-start", NULL);
 
 	/*
 	 * Compute the multixact age for which freezing is urgent.  This is
diff --git a/src/backend/storage/aio/aio.c b/src/backend/storage/aio/aio.c
index 86f7250b7a5f..366cbc23d6b7 100644
--- a/src/backend/storage/aio/aio.c
+++ b/src/backend/storage/aio/aio.c
@@ -1245,7 +1245,7 @@ pgaio_io_call_inj(PgAioHandle *ioh, const char *injection_point)
 
 	PG_TRY();
 	{
-		InjectionPointCached(injection_point);
+		InjectionPointCached(injection_point, NULL);
 	}
 	PG_FINALLY();
 	{
diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c
index dde8d5b35175..a7d1fec981f8 100644
--- a/src/backend/tcop/backend_startup.c
+++ b/src/backend/tcop/backend_startup.c
@@ -234,7 +234,7 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
 
 	/* For testing client error handling */
 #ifdef USE_INJECTION_POINTS
-	INJECTION_POINT("backend-initialize");
+	INJECTION_POINT("backend-initialize", NULL);
 	if (IS_INJECTION_POINT_ATTACHED("backend-initialize-v2-error"))
 	{
 		/*
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dc4c600922df..1ae51b1b3915 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3482,7 +3482,7 @@ ProcessInterrupts(void)
 		IdleInTransactionSessionTimeoutPending = false;
 		if (IdleInTransactionSessionTimeout > 0)
 		{
-			INJECTION_POINT("idle-in-transaction-session-timeout");
+			INJECTION_POINT("idle-in-transaction-session-timeout", NULL);
 			ereport(FATAL,
 					(errcode(ERRCODE_IDLE_IN_TRANSACTION_SESSION_TIMEOUT),
 					 errmsg("terminating connection due to idle-in-transaction timeout")));
@@ -3495,7 +3495,7 @@ ProcessInterrupts(void)
 		TransactionTimeoutPending = false;
 		if (TransactionTimeout > 0)
 		{
-			INJECTION_POINT("transaction-timeout");
+			INJECTION_POINT("transaction-timeout", NULL);
 			ereport(FATAL,
 					(errcode(ERRCODE_TRANSACTION_TIMEOUT),
 					 errmsg("terminating connection due to transaction timeout")));
@@ -3508,7 +3508,7 @@ ProcessInterrupts(void)
 		IdleSessionTimeoutPending = false;
 		if (IdleSessionTimeout > 0)
 		{
-			INJECTION_POINT("idle-session-timeout");
+			INJECTION_POINT("idle-session-timeout", NULL);
 			ereport(FATAL,
 					(errcode(ERRCODE_IDLE_SESSION_TIMEOUT),
 					 errmsg("terminating connection due to idle-session timeout")));
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 9ad7681f1555..13b6f104146d 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1904,7 +1904,7 @@ SearchCatCacheList(CatCache *cache,
 			/* Injection point to help testing the recursive invalidation case */
 			if (first_iter)
 			{
-				INJECTION_POINT("catcache-list-miss-systable-scan-started");
+				INJECTION_POINT("catcache-list-miss-systable-scan-started", NULL);
 				first_iter = false;
 			}
 
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 4893dbdd7c8b..5f5e5c9f9d08 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -1200,7 +1200,7 @@ AtEOXact_Inval(bool isCommit)
 	/* Must be at top of stack */
 	Assert(transInvalInfo->my_level == 1 && transInvalInfo->parent == NULL);
 
-	INJECTION_POINT("AtEOXact_Inval-with-transInvalInfo");
+	INJECTION_POINT("AtEOXact_Inval-with-transInvalInfo", NULL);
 
 	if (isCommit)
 	{
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index ae65a1cce06c..07e4e6284958 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -952,7 +952,7 @@ lookup_type_cache(Oid type_id, int flags)
 		load_domaintype_info(typentry);
 	}
 
-	INJECTION_POINT("typecache-before-rel-type-cache-insert");
+	INJECTION_POINT("typecache-before-rel-type-cache-insert", NULL);
 
 	Assert(in_progress_offset + 1 == in_progress_list_len);
 	in_progress_list_len--;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 01309ef3f867..b7856670a0db 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -751,7 +751,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
 	if (!bootstrap)
 	{
 		pgstat_bestart_initial();
-		INJECTION_POINT("init-pre-auth");
+		INJECTION_POINT("init-pre-auth", NULL);
 	}
 
 	/*
diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c
index 97ab851f0a63..389a71910ed7 100644
--- a/src/backend/utils/misc/injection_point.c
+++ b/src/backend/utils/misc/injection_point.c
@@ -541,14 +541,14 @@ InjectionPointLoad(const char *name)
  * Execute an injection point, if defined.
  */
 void
-InjectionPointRun(const char *name)
+InjectionPointRun(const char *name, const void *arg)
 {
 #ifdef USE_INJECTION_POINTS
 	InjectionPointCacheEntry *cache_entry;
 
 	cache_entry = InjectionPointCacheRefresh(name);
 	if (cache_entry)
-		cache_entry->callback(name, cache_entry->private_data);
+		cache_entry->callback(name, cache_entry->private_data, arg);
 #else
 	elog(ERROR, "Injection points are not supported by this build");
 #endif
@@ -558,14 +558,14 @@ InjectionPointRun(const char *name)
  * Execute an injection point directly from the cache, if defined.
  */
 void
-InjectionPointCached(const char *name)
+InjectionPointCached(const char *name, const void *arg)
 {
 #ifdef USE_INJECTION_POINTS
 	InjectionPointCacheEntry *cache_entry;
 
 	cache_entry = injection_point_cache_get(name);
 	if (cache_entry)
-		cache_entry->callback(name, cache_entry->private_data);
+		cache_entry->callback(name, cache_entry->private_data, arg);
 #else
 	elog(ERROR, "Injection points are not supported by this build");
 #endif
diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index ad528d77752b..027ef56964d1 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -405,7 +405,7 @@ injection_points_run(PG_FUNCTION_ARGS)
 	char	   *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
 
 	pgstat_report_inj_fixed(0, 0, 1, 0, 0);
-	INJECTION_POINT(name);
+	INJECTION_POINT(name, NULL);
 
 	PG_RETURN_VOID();
 }
@@ -420,7 +420,7 @@ injection_points_cached(PG_FUNCTION_ARGS)
 	char	   *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
 
 	pgstat_report_inj_fixed(0, 0, 0, 1, 0);
-	INJECTION_POINT_CACHED(name);
+	INJECTION_POINT_CACHED(name, NULL);
 
 	PG_RETURN_VOID();
 }
diff --git a/src/test/modules/test_slru/test_multixact.c b/src/test/modules/test_slru/test_multixact.c
index a9b9628c9587..6c9b0420717c 100644
--- a/src/test/modules/test_slru/test_multixact.c
+++ b/src/test/modules/test_slru/test_multixact.c
@@ -46,7 +46,7 @@ test_read_multixact(PG_FUNCTION_ARGS)
 	MultiXactId id = PG_GETARG_TRANSACTIONID(0);
 	MultiXactMember *members;
 
-	INJECTION_POINT("test-multixact-read");
+	INJECTION_POINT("test-multixact-read", NULL);
 	/* discard caches */
 	AtEOXact_MultiXact();
 
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 8074f66417da..451af81b8523 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -3829,14 +3829,15 @@ uint32 WaitEventExtensionNew(const char *wait_event_name)
      An injection point with a given <literal>name</literal> is declared using
      macro:
 <programlisting>
-INJECTION_POINT(name);
+INJECTION_POINT(name, arg);
 </programlisting>
 
      There are a few injection points already declared at strategic points
      within the server code. After adding a new injection point the code needs
      to be compiled in order for that injection point to be available in the
      binary. Add-ins written in C-language can declare injection points in
-     their own code using the same macro.
+     their own code using the same macro. <literal>arg</literal> is an optional
+     argument value given to the callback at run-time.
     </para>
 
     <para>
@@ -3846,7 +3847,7 @@ INJECTION_POINT(name);
      a two-step approach with the following macros:
 <programlisting>
 INJECTION_POINT_LOAD(name);
-INJECTION_POINT_CACHED(name);
+INJECTION_POINT_CACHED(name, arg);
 </programlisting>
 
      Before entering the critical section,
@@ -3879,7 +3880,9 @@ extern void InjectionPointAttach(const char *name,
      <literal>InjectionPointCallback</literal>:
 <programlisting>
 static void
-custom_injection_callback(const char *name, const void *private_data)
+custom_injection_callback(const char *name,
+                          const void *private_data,
+                          const void *arg)
 {
     uint32 wait_event_info = WaitEventInjectionPointNew(name);
 
-- 
2.49.0

From 7dbeb7f25bad05d1a4584913c84181e3ad410dc3 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Mon, 14 Apr 2025 15:46:32 +0900
Subject: [PATCH 2/3] injection_points: Add support for runtime arguments

This provides test coverage for run-time arguments for the cached and
run paths.
---
 .../expected/injection_points.out             | 45 ++++++++++++++++
 .../injection_points--1.0.sql                 | 10 ++--
 .../injection_points/injection_points.c       | 53 ++++++++++++++-----
 .../injection_points/sql/injection_points.sql | 10 ++++
 4 files changed, 102 insertions(+), 16 deletions(-)

diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out
index f25bbe4966ee..43bcdd01582f 100644
--- a/src/test/modules/injection_points/expected/injection_points.out
+++ b/src/test/modules/injection_points/expected/injection_points.out
@@ -6,6 +6,19 @@ CREATE FUNCTION wait_pid(int)
   RETURNS void
   AS :'regresslib'
   LANGUAGE C STRICT;
+-- Non-strict checks
+SELECT injection_points_run(NULL);
+ injection_points_run 
+----------------------
+ 
+(1 row)
+
+SELECT injection_points_cached(NULL);
+ injection_points_cached 
+-------------------------
+ 
+(1 row)
+
 SELECT injection_points_attach('TestInjectionBooh', 'booh');
 ERROR:  incorrect action "booh" for injection point creation
 SELECT injection_points_attach('TestInjectionError', 'error');
@@ -39,6 +52,20 @@ NOTICE:  notice triggered for injection point TestInjectionLog2
  
 (1 row)
 
+SELECT injection_points_run('TestInjectionLog2', NULL); -- notice
+NOTICE:  notice triggered for injection point TestInjectionLog2
+ injection_points_run 
+----------------------
+ 
+(1 row)
+
+SELECT injection_points_run('TestInjectionLog2', 'foobar'); -- notice + arg
+NOTICE:  notice triggered for injection point TestInjectionLog2 (foobar)
+ injection_points_run 
+----------------------
+ 
+(1 row)
+
 SELECT injection_points_run('TestInjectionLog'); -- notice
 NOTICE:  notice triggered for injection point TestInjectionLog
  injection_points_run 
@@ -48,6 +75,10 @@ NOTICE:  notice triggered for injection point TestInjectionLog
 
 SELECT injection_points_run('TestInjectionError'); -- error
 ERROR:  error triggered for injection point TestInjectionError
+SELECT injection_points_run('TestInjectionError', NULL); -- error
+ERROR:  error triggered for injection point TestInjectionError
+SELECT injection_points_run('TestInjectionError', 'foobar2'); -- error + arg
+ERROR:  error triggered for injection point TestInjectionError (foobar2)
 -- Re-load cache and run again.
 \c
 SELECT injection_points_run('TestInjectionLog2'); -- notice
@@ -160,6 +191,20 @@ NOTICE:  notice triggered for injection point TestInjectionLogLoad
  
 (1 row)
 
+SELECT injection_points_cached('TestInjectionLogLoad', NULL); -- runs from cache
+NOTICE:  notice triggered for injection point TestInjectionLogLoad
+ injection_points_cached 
+-------------------------
+ 
+(1 row)
+
+SELECT injection_points_cached('TestInjectionLogLoad', 'foobar'); -- runs from cache
+NOTICE:  notice triggered for injection point TestInjectionLogLoad (foobar)
+ injection_points_cached 
+-------------------------
+ 
+(1 row)
+
 SELECT injection_points_run('TestInjectionLogLoad'); -- runs from cache
 NOTICE:  notice triggered for injection point TestInjectionLogLoad
  injection_points_run 
diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/src/test/modules/injection_points/injection_points--1.0.sql
index 5d83f08811b0..cc76b1bf99ae 100644
--- a/src/test/modules/injection_points/injection_points--1.0.sql
+++ b/src/test/modules/injection_points/injection_points--1.0.sql
@@ -29,20 +29,22 @@ LANGUAGE C STRICT PARALLEL UNSAFE;
 --
 -- Executes the action attached to the injection point.
 --
-CREATE FUNCTION injection_points_run(IN point_name TEXT)
+CREATE FUNCTION injection_points_run(IN point_name TEXT,
+    IN arg TEXT DEFAULT NULL)
 RETURNS void
 AS 'MODULE_PATHNAME', 'injection_points_run'
-LANGUAGE C STRICT PARALLEL UNSAFE;
+LANGUAGE C PARALLEL UNSAFE;
 
 --
 -- injection_points_cached()
 --
 -- Executes the action attached to the injection point, from local cache.
 --
-CREATE FUNCTION injection_points_cached(IN point_name TEXT)
+CREATE FUNCTION injection_points_cached(IN point_name TEXT,
+    IN arg TEXT DEFAULT NULL)
 RETURNS void
 AS 'MODULE_PATHNAME', 'injection_points_cached'
-LANGUAGE C STRICT PARALLEL UNSAFE;
+LANGUAGE C PARALLEL UNSAFE;
 
 --
 -- injection_points_wakeup()
diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c
index 027ef56964d1..4187f3096dd4 100644
--- a/src/test/modules/injection_points/injection_points.c
+++ b/src/test/modules/injection_points/injection_points.c
@@ -94,11 +94,14 @@ typedef struct InjectionPointSharedState
 static InjectionPointSharedState *inj_state = NULL;
 
 extern PGDLLEXPORT void injection_error(const char *name,
-										const void *private_data);
+										const void *private_data,
+										const void *arg);
 extern PGDLLEXPORT void injection_notice(const char *name,
-										 const void *private_data);
+										 const void *private_data,
+										 const void *arg);
 extern PGDLLEXPORT void injection_wait(const char *name,
-									   const void *private_data);
+									   const void *private_data,
+									   const void *arg);
 
 /* track if injection points attached in this process are linked to it */
 static bool injection_point_local = false;
@@ -239,34 +242,44 @@ 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)
+injection_error(const char *name, const void *private_data, const void *arg)
 {
 	InjectionPointCondition *condition = (InjectionPointCondition *) private_data;
+	char	   *argstr = (char *) arg;
 
 	if (!injection_point_allowed(condition))
 		return;
 
 	pgstat_report_inj(name);
 
-	elog(ERROR, "error triggered for injection point %s", name);
+	if (argstr)
+		elog(ERROR, "error triggered for injection point %s (%s)",
+			 name, argstr);
+	else
+		elog(ERROR, "error triggered for injection point %s", name);
 }
 
 void
-injection_notice(const char *name, const void *private_data)
+injection_notice(const char *name, const void *private_data, const void *arg)
 {
 	InjectionPointCondition *condition = (InjectionPointCondition *) private_data;
+	char	   *argstr = (char *) arg;
 
 	if (!injection_point_allowed(condition))
 		return;
 
 	pgstat_report_inj(name);
 
-	elog(NOTICE, "notice triggered for injection point %s", name);
+	if (argstr)
+		elog(NOTICE, "notice triggered for injection point %s (%s)",
+			 name, argstr);
+	else
+		elog(NOTICE, "notice triggered for injection point %s", name);
 }
 
 /* Wait on a condition variable, awaken by injection_points_wakeup() */
 void
-injection_wait(const char *name, const void *private_data)
+injection_wait(const char *name, const void *private_data, const void *arg)
 {
 	uint32		old_wait_counts = 0;
 	int			index = -1;
@@ -402,10 +415,18 @@ PG_FUNCTION_INFO_V1(injection_points_run);
 Datum
 injection_points_run(PG_FUNCTION_ARGS)
 {
-	char	   *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	   *name;
+	char	   *arg = NULL;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_VOID();
+	name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+
+	if (!PG_ARGISNULL(1))
+		arg = text_to_cstring(PG_GETARG_TEXT_PP(1));
 
 	pgstat_report_inj_fixed(0, 0, 1, 0, 0);
-	INJECTION_POINT(name, NULL);
+	INJECTION_POINT(name, arg);
 
 	PG_RETURN_VOID();
 }
@@ -417,10 +438,18 @@ PG_FUNCTION_INFO_V1(injection_points_cached);
 Datum
 injection_points_cached(PG_FUNCTION_ARGS)
 {
-	char	   *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	char	   *name;
+	char	   *arg = NULL;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_VOID();
+	name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+
+	if (!PG_ARGISNULL(1))
+		arg = text_to_cstring(PG_GETARG_TEXT_PP(1));
 
 	pgstat_report_inj_fixed(0, 0, 0, 1, 0);
-	INJECTION_POINT_CACHED(name, NULL);
+	INJECTION_POINT_CACHED(name, arg);
 
 	PG_RETURN_VOID();
 }
diff --git a/src/test/modules/injection_points/sql/injection_points.sql b/src/test/modules/injection_points/sql/injection_points.sql
index e3a481d60449..d9748331c771 100644
--- a/src/test/modules/injection_points/sql/injection_points.sql
+++ b/src/test/modules/injection_points/sql/injection_points.sql
@@ -9,6 +9,10 @@ CREATE FUNCTION wait_pid(int)
   AS :'regresslib'
   LANGUAGE C STRICT;
 
+-- Non-strict checks
+SELECT injection_points_run(NULL);
+SELECT injection_points_cached(NULL);
+
 SELECT injection_points_attach('TestInjectionBooh', 'booh');
 SELECT injection_points_attach('TestInjectionError', 'error');
 SELECT injection_points_attach('TestInjectionLog', 'notice');
@@ -16,8 +20,12 @@ SELECT injection_points_attach('TestInjectionLog2', 'notice');
 
 SELECT injection_points_run('TestInjectionBooh'); -- nothing
 SELECT injection_points_run('TestInjectionLog2'); -- notice
+SELECT injection_points_run('TestInjectionLog2', NULL); -- notice
+SELECT injection_points_run('TestInjectionLog2', 'foobar'); -- notice + arg
 SELECT injection_points_run('TestInjectionLog'); -- notice
 SELECT injection_points_run('TestInjectionError'); -- error
+SELECT injection_points_run('TestInjectionError', NULL); -- error
+SELECT injection_points_run('TestInjectionError', 'foobar2'); -- error + arg
 
 -- Re-load cache and run again.
 \c
@@ -47,6 +55,8 @@ SELECT injection_points_load('TestInjectionLogLoad'); -- nothing
 SELECT injection_points_attach('TestInjectionLogLoad', 'notice');
 SELECT injection_points_load('TestInjectionLogLoad'); -- nothing happens
 SELECT injection_points_cached('TestInjectionLogLoad'); -- runs from cache
+SELECT injection_points_cached('TestInjectionLogLoad', NULL); -- runs from cache
+SELECT injection_points_cached('TestInjectionLogLoad', 'foobar'); -- runs from cache
 SELECT injection_points_run('TestInjectionLogLoad'); -- runs from cache
 SELECT injection_points_detach('TestInjectionLogLoad');
 
-- 
2.49.0

From f47e88ba13bce1857f61d5b5d4517640a754f8ad Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Mon, 14 Apr 2025 16:24:54 +0900
Subject: [PATCH 3/3] aio: Use runtime arguments with injections points

This cleans up some code related to AIO for injection points, switching
the test code to use the in-core support rather than tweaks to pass
arguments to the callbacks run.
---
 src/include/storage/aio_internal.h      | 20 ---------
 src/backend/storage/aio/aio.c           | 58 +------------------------
 src/backend/storage/aio/method_worker.c |  3 +-
 src/test/modules/test_aio/test_aio.c    | 16 ++++---
 4 files changed, 13 insertions(+), 84 deletions(-)

diff --git a/src/include/storage/aio_internal.h b/src/include/storage/aio_internal.h
index 33f27b9fe508..2d37a243abe5 100644
--- a/src/include/storage/aio_internal.h
+++ b/src/include/storage/aio_internal.h
@@ -394,26 +394,6 @@ extern const char *pgaio_io_get_target_name(PgAioHandle *ioh);
 				pgaio_io_get_state_name(ioh), \
 				__VA_ARGS__)
 
-
-#ifdef USE_INJECTION_POINTS
-
-extern void pgaio_io_call_inj(PgAioHandle *ioh, const char *injection_point);
-
-/* just for use in tests, from within injection points */
-extern PgAioHandle *pgaio_inj_io_get(void);
-
-#else
-
-#define pgaio_io_call_inj(ioh, injection_point) (void) 0
-
-/*
- * no fallback for pgaio_inj_io_get, all code using injection points better be
- * guarded by USE_INJECTION_POINTS.
- */
-
-#endif
-
-
 /* Declarations for the tables of function pointers exposed by each IO method. */
 extern PGDLLIMPORT const IoMethodOps pgaio_sync_ops;
 extern PGDLLIMPORT const IoMethodOps pgaio_worker_ops;
diff --git a/src/backend/storage/aio/aio.c b/src/backend/storage/aio/aio.c
index 366cbc23d6b7..cfb2b3301106 100644
--- a/src/backend/storage/aio/aio.c
+++ b/src/backend/storage/aio/aio.c
@@ -46,13 +46,10 @@
 #include "storage/aio_subsys.h"
 #include "utils/guc.h"
 #include "utils/guc_hooks.h"
+#include "utils/injection_point.h"
 #include "utils/resowner.h"
 #include "utils/wait_event_types.h"
 
-#ifdef USE_INJECTION_POINTS
-#include "utils/injection_point.h"
-#endif
-
 
 static inline void pgaio_io_update_state(PgAioHandle *ioh, PgAioHandleState new_state);
 static void pgaio_io_reclaim(PgAioHandle *ioh);
@@ -96,17 +93,6 @@ static const IoMethodOps *const pgaio_method_ops_table[] = {
 const IoMethodOps *pgaio_method_ops;
 
 
-/*
- * Currently there's no infrastructure to pass arguments to injection points,
- * so we instead set this up for the duration of the injection point
- * invocation. See pgaio_io_call_inj().
- */
-#ifdef USE_INJECTION_POINTS
-static PgAioHandle *pgaio_inj_cur_handle;
-#endif
-
-
-
 /* --------------------------------------------------------------------------------
  * Public Functions related to PgAioHandle
  * --------------------------------------------------------------------------------
@@ -507,7 +493,7 @@ pgaio_io_process_completion(PgAioHandle *ioh, int result)
 
 	pgaio_io_update_state(ioh, PGAIO_HS_COMPLETED_IO);
 
-	pgaio_io_call_inj(ioh, "AIO_PROCESS_COMPLETION_BEFORE_SHARED");
+	INJECTION_POINT("AIO_PROCESS_COMPLETION_BEFORE_SHARED", ioh);
 
 	pgaio_io_call_complete_shared(ioh);
 
@@ -1225,43 +1211,3 @@ check_io_max_concurrency(int *newval, void **extra, GucSource source)
 
 	return true;
 }
-
-
-
-/* --------------------------------------------------------------------------------
- * Injection point support
- * --------------------------------------------------------------------------------
- */
-
-#ifdef USE_INJECTION_POINTS
-
-/*
- * Call injection point with support for pgaio_inj_io_get().
- */
-void
-pgaio_io_call_inj(PgAioHandle *ioh, const char *injection_point)
-{
-	pgaio_inj_cur_handle = ioh;
-
-	PG_TRY();
-	{
-		InjectionPointCached(injection_point, NULL);
-	}
-	PG_FINALLY();
-	{
-		pgaio_inj_cur_handle = NULL;
-	}
-	PG_END_TRY();
-}
-
-/*
- * Return IO associated with injection point invocation. This is only needed
- * as injection points currently don't support arguments.
- */
-PgAioHandle *
-pgaio_inj_io_get(void)
-{
-	return pgaio_inj_cur_handle;
-}
-
-#endif
diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c
index 8ad17ec1ef73..6779fb9b7f77 100644
--- a/src/backend/storage/aio/method_worker.c
+++ b/src/backend/storage/aio/method_worker.c
@@ -42,6 +42,7 @@
 #include "storage/latch.h"
 #include "storage/proc.h"
 #include "tcop/tcopprot.h"
+#include "utils/injection_point.h"
 #include "utils/memdebug.h"
 #include "utils/ps_status.h"
 #include "utils/wait_event.h"
@@ -525,7 +526,7 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len)
 			 * To be able to exercise the reopen-fails path, allow injection
 			 * points to trigger a failure at this point.
 			 */
-			pgaio_io_call_inj(ioh, "AIO_WORKER_AFTER_REOPEN");
+			INJECTION_POINT("AIO_WORKER_AFTER_REOPEN", ioh);
 
 			error_errno = 0;
 			error_ioh = NULL;
diff --git a/src/test/modules/test_aio/test_aio.c b/src/test/modules/test_aio/test_aio.c
index 1d776010ef41..07711e46ecbf 100644
--- a/src/test/modules/test_aio/test_aio.c
+++ b/src/test/modules/test_aio/test_aio.c
@@ -674,13 +674,17 @@ batch_end(PG_FUNCTION_ARGS)
 }
 
 #ifdef USE_INJECTION_POINTS
-extern PGDLLEXPORT void inj_io_short_read(const char *name, const void *private_data);
-extern PGDLLEXPORT void inj_io_reopen(const char *name, const void *private_data);
+extern PGDLLEXPORT void inj_io_short_read(const char *name,
+										  const void *private_data,
+										  const void *arg);
+extern PGDLLEXPORT void inj_io_reopen(const char *name,
+									  const void *private_data,
+									  const void *arg);
 
 void
-inj_io_short_read(const char *name, const void *private_data)
+inj_io_short_read(const char *name, const void *private_data, const void *arg)
 {
-	PgAioHandle *ioh;
+	PgAioHandle *ioh = (PgAioHandle *) arg;
 
 	ereport(LOG,
 			errmsg("short read injection point called, is enabled: %d",
@@ -689,8 +693,6 @@ inj_io_short_read(const char *name, const void *private_data)
 
 	if (inj_io_error_state->enabled_short_read)
 	{
-		ioh = pgaio_inj_io_get();
-
 		/*
 		 * Only shorten reads that are actually longer than the target size,
 		 * otherwise we can trigger over-reads.
@@ -742,7 +744,7 @@ inj_io_short_read(const char *name, const void *private_data)
 }
 
 void
-inj_io_reopen(const char *name, const void *private_data)
+inj_io_reopen(const char *name, const void *private_data, const void *arg)
 {
 	ereport(LOG,
 			errmsg("reopen injection point called, is enabled: %d",
-- 
2.49.0

Attachment: signature.asc
Description: PGP signature

Reply via email to