diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 7d053698a2b..a7c170476af 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -3624,12 +3624,16 @@ INJECTION_POINT(name); extern void InjectionPointAttach(const char *name, const char *library, - const char *function); + const char *function, + const void *private_data, + int private_data_size); name is the name of the injection point, which when reached during execution will execute the function - loaded from library. + loaded from library. private_data + is a private area of data of size private_data_size + given as argument to the callback when executed. @@ -3637,7 +3641,7 @@ extern void InjectionPointAttach(const char *name, InjectionPointCallback: static void -custom_injection_callback(const char *name) +custom_injection_callback(const char *name, const void *private_data) { elog(NOTICE, "%s: executed custom callback", name); } @@ -3650,8 +3654,10 @@ custom_injection_callback(const char *name) Optionally, it is possible to detach an injection point by calling: -extern void InjectionPointDetach(const char *name); +extern bool InjectionPointDetach(const char *name); + On success, true is returned, false + otherwise. diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c index 0cf4d51cac4..5c2a0d2297e 100644 --- a/src/backend/utils/misc/injection_point.c +++ b/src/backend/utils/misc/injection_point.c @@ -42,6 +42,7 @@ static HTAB *InjectionPointHash; /* find points from names */ #define INJ_NAME_MAXLEN 64 #define INJ_LIB_MAXLEN 128 #define INJ_FUNC_MAXLEN 128 +#define INJ_PRIVATE_MAXLEN 1024 /* Single injection point stored in InjectionPointHash */ typedef struct InjectionPointEntry @@ -49,6 +50,12 @@ typedef struct InjectionPointEntry char name[INJ_NAME_MAXLEN]; /* hash key */ char library[INJ_LIB_MAXLEN]; /* library */ char function[INJ_FUNC_MAXLEN]; /* function */ + + /* + * Opaque data area that modules can use to pass some custom data to + * callbacks, registered when attached. + */ + char private_data[INJ_PRIVATE_MAXLEN]; } InjectionPointEntry; #define INJECTION_POINT_HASH_INIT_SIZE 16 @@ -61,6 +68,7 @@ typedef struct InjectionPointEntry typedef struct InjectionPointCacheEntry { char name[INJ_NAME_MAXLEN]; + char private_data[INJ_PRIVATE_MAXLEN]; InjectionPointCallback callback; } InjectionPointCacheEntry; @@ -73,7 +81,8 @@ static HTAB *InjectionPointCache = NULL; */ static void injection_point_cache_add(const char *name, - InjectionPointCallback callback) + InjectionPointCallback callback, + const void *private_data) { InjectionPointCacheEntry *entry; bool found; @@ -99,6 +108,8 @@ injection_point_cache_add(const char *name, Assert(!found); strlcpy(entry->name, name, sizeof(entry->name)); entry->callback = callback; + if (private_data != NULL) + memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN); } /* @@ -124,11 +135,14 @@ injection_point_cache_remove(const char *name) * Retrieve an injection point from the local cache, if any. */ static InjectionPointCallback -injection_point_cache_get(const char *name) +injection_point_cache_get(const char *name, const void **private_data) { bool found; InjectionPointCacheEntry *entry; + if (private_data) + *private_data = NULL; + /* no callback if no cache yet */ if (InjectionPointCache == NULL) return NULL; @@ -137,7 +151,11 @@ injection_point_cache_get(const char *name) hash_search(InjectionPointCache, name, HASH_FIND, &found); if (found) + { + if (private_data) + *private_data = entry->private_data; return entry->callback; + } return NULL; } @@ -186,7 +204,9 @@ InjectionPointShmemInit(void) void InjectionPointAttach(const char *name, const char *library, - const char *function) + const char *function, + const void *private_data, + int private_data_size) { #ifdef USE_INJECTION_POINTS InjectionPointEntry *entry_by_name; @@ -201,6 +221,9 @@ InjectionPointAttach(const char *name, if (strlen(function) >= INJ_FUNC_MAXLEN) elog(ERROR, "injection point function %s too long (maximum of %u)", function, INJ_FUNC_MAXLEN); + if (private_data_size >= INJ_PRIVATE_MAXLEN) + elog(ERROR, "injection point data too long (maximum of %u)", + INJ_PRIVATE_MAXLEN); /* * Allocate and register a new injection point. A new point should not @@ -223,6 +246,8 @@ InjectionPointAttach(const char *name, entry_by_name->library[INJ_LIB_MAXLEN - 1] = '\0'; strlcpy(entry_by_name->function, function, sizeof(entry_by_name->function)); entry_by_name->function[INJ_FUNC_MAXLEN - 1] = '\0'; + if (private_data != NULL) + memcpy(entry_by_name->private_data, private_data, private_data_size); LWLockRelease(InjectionPointLock); @@ -233,8 +258,10 @@ InjectionPointAttach(const char *name, /* * Detach an existing injection point. + * + * Returns true if the injection point was detached, false otherwise. */ -void +bool InjectionPointDetach(const char *name) { #ifdef USE_INJECTION_POINTS @@ -245,10 +272,12 @@ InjectionPointDetach(const char *name) LWLockRelease(InjectionPointLock); if (!found) - elog(ERROR, "injection point \"%s\" not found", name); + return false; + return true; #else elog(ERROR, "Injection points are not supported by this build"); + return true; /* silence compiler */ #endif } @@ -265,6 +294,7 @@ InjectionPointRun(const char *name) InjectionPointEntry *entry_by_name; bool found; InjectionPointCallback injection_callback; + const void *private_data; LWLockAcquire(InjectionPointLock, LW_SHARED); entry_by_name = (InjectionPointEntry *) @@ -286,10 +316,10 @@ InjectionPointRun(const char *name) * Check if the callback exists in the local cache, to avoid unnecessary * external loads. */ - injection_callback = injection_point_cache_get(name); - if (injection_callback == NULL) + if (injection_point_cache_get(name, NULL) == NULL) { char path[MAXPGPATH]; + InjectionPointCallback injection_callback_local; /* not found in local cache, so load and register */ snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path, @@ -299,18 +329,21 @@ InjectionPointRun(const char *name) elog(ERROR, "could not find library \"%s\" for injection point \"%s\"", path, name); - injection_callback = (InjectionPointCallback) + injection_callback_local = (InjectionPointCallback) load_external_function(path, entry_by_name->function, false, NULL); - if (injection_callback == NULL) + if (injection_callback_local == NULL) elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"", entry_by_name->function, path, name); /* add it to the local cache when found */ - injection_point_cache_add(name, injection_callback); + injection_point_cache_add(name, injection_callback_local, + entry_by_name->private_data); } - injection_callback(name); + /* Now loaded, so get it. */ + injection_callback = injection_point_cache_get(name, &private_data); + injection_callback(name, private_data); #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 55524b568ff..a61d5d44391 100644 --- a/src/include/utils/injection_point.h +++ b/src/include/utils/injection_point.h @@ -23,15 +23,18 @@ /* * Typedef for callback function launched by an injection point. */ -typedef void (*InjectionPointCallback) (const char *name); +typedef void (*InjectionPointCallback) (const char *name, + const void *private_data); extern Size InjectionPointShmemSize(void); extern void InjectionPointShmemInit(void); extern void InjectionPointAttach(const char *name, const char *library, - const char *function); + const char *function, + const void *private_data, + int private_data_size); extern void InjectionPointRun(const char *name); -extern void InjectionPointDetach(const char *name); +extern bool InjectionPointDetach(const char *name); #endif /* INJECTION_POINT_H */ diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out index 1341d66c927..dd9db06e10b 100644 --- a/src/test/modules/injection_points/expected/injection_points.out +++ b/src/test/modules/injection_points/expected/injection_points.out @@ -114,7 +114,7 @@ NOTICE: notice triggered for injection point TestInjectionLog2 (1 row) SELECT injection_points_detach('TestInjectionLog'); -- fails -ERROR: injection point "TestInjectionLog" not found +ERROR: could not detach injection point "TestInjectionLog" SELECT injection_points_run('TestInjectionLog2'); -- notice NOTICE: notice triggered for injection point TestInjectionLog2 injection_points_run diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index a74a4a28afd..3126c8117c6 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -73,9 +73,12 @@ typedef struct InjectionPointSharedState /* Pointer to shared-memory state. */ static InjectionPointSharedState *inj_state = NULL; -extern PGDLLEXPORT void injection_error(const char *name); -extern PGDLLEXPORT void injection_notice(const char *name); -extern PGDLLEXPORT void injection_wait(const char *name); +extern PGDLLEXPORT void injection_error(const char *name, + const void *private_data); +extern PGDLLEXPORT void injection_notice(const char *name, + const void *private_data); +extern PGDLLEXPORT void injection_wait(const char *name, + const void *private_data); /* track if injection points attached in this process are linked to it */ static bool injection_point_local = false; @@ -189,7 +192,7 @@ injection_points_cleanup(int code, Datum arg) /* Detach, without holding the spinlock */ for (int i = 0; i < count; i++) - InjectionPointDetach(names[i]); + (void) InjectionPointDetach(names[i]); /* Clear all the conditions */ SpinLockAcquire(&inj_state->lock); @@ -211,7 +214,7 @@ injection_points_cleanup(int code, Datum arg) /* Set of callbacks available to be attached to an injection point. */ void -injection_error(const char *name) +injection_error(const char *name, const void *private_data) { if (!injection_point_allowed(name)) return; @@ -220,7 +223,7 @@ injection_error(const char *name) } void -injection_notice(const char *name) +injection_notice(const char *name, const void *private_data) { if (!injection_point_allowed(name)) return; @@ -230,7 +233,7 @@ injection_notice(const char *name) /* Wait on a condition variable, awaken by injection_points_wakeup() */ void -injection_wait(const char *name) +injection_wait(const char *name, const void *private_data) { uint32 old_wait_counts = 0; int index = -1; @@ -311,7 +314,7 @@ injection_points_attach(PG_FUNCTION_ARGS) else elog(ERROR, "incorrect action \"%s\" for injection point creation", action); - InjectionPointAttach(name, "injection_points", function); + InjectionPointAttach(name, "injection_points", function, NULL, 0); if (injection_point_local) { @@ -430,7 +433,8 @@ injection_points_detach(PG_FUNCTION_ARGS) { char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); - InjectionPointDetach(name); + if (!InjectionPointDetach(name)) + elog(ERROR, "could not detach injection point \"%s\"", name); if (inj_state == NULL) injection_init_shmem();