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
