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();