diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index c67688cbf5f9..8e38d429c776 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -28496,6 +28496,54 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} + + Injection Points Information Functions + + + The functions shown in + print information about the injection points. + See . + + + + WAL Summarization Information Functions + + + + + Function + + + Description + + + + + + + + + pg_get_injection_points + + pg_get_injection_points () + setof record + ( name text, + library text, + function text ) + + + Returns information about the injection points currently attached + to the cluster, if the server was built with the configure option + ; otherwise throws + an error. + + + + + +
+
+ diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index b362ae437710..93703633f69a 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -22,6 +22,7 @@ OBJS = \ guc_tables.o \ help_config.o \ injection_point.o \ + injection_point_funcs.o \ pg_config.o \ pg_controldata.o \ pg_rusage.o \ diff --git a/src/backend/utils/misc/injection_point.c b/src/backend/utils/misc/injection_point.c index f58ebc8ee522..12570fba56e4 100644 --- a/src/backend/utils/misc/injection_point.c +++ b/src/backend/utils/misc/injection_point.c @@ -584,3 +584,49 @@ IsInjectionPointAttached(const char *name) return false; /* silence compiler */ #endif } + +/* + * Retrieve a list of all the injection points currently attached. + * + * This list is palloc'd in the current memory context. + */ +List * +InjectionPointList(void) +{ +#ifdef USE_INJECTION_POINTS + List *inj_points = NIL; + uint32 max_inuse; + + LWLockAcquire(InjectionPointLock, LW_SHARED); + + max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse); + + for (uint32 idx = 0; idx < max_inuse; idx++) + { + InjectionPointEntry *entry; + InjectionPointData *inj_point; + uint64 generation; + + entry = &ActiveInjectionPoints->entries[idx]; + generation = pg_atomic_read_u64(&entry->generation); + + /* skip free slots */ + if (generation % 2 == 0) + continue; + + inj_point = (InjectionPointData *) palloc0(sizeof(InjectionPointData)); + inj_point->name = pstrdup(entry->name); + inj_point->library = pstrdup(entry->library); + inj_point->function = pstrdup(entry->function); + inj_points = lappend(inj_points, inj_point); + } + + LWLockRelease(InjectionPointLock); + + return inj_points; + +#else + elog(ERROR, "Injection points are not supported by this build"); + return NIL; /* keep compiler quiet */ +#endif +} diff --git a/src/backend/utils/misc/injection_point_funcs.c b/src/backend/utils/misc/injection_point_funcs.c new file mode 100644 index 000000000000..c7cd02a517da --- /dev/null +++ b/src/backend/utils/misc/injection_point_funcs.c @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * injection_point_funcs.c + * + * SQL commands and SQL-accessible functions related to injection points. + * + * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/misc/injection_point_funcs.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "funcapi.h" +#include "utils/builtins.h" +#include "utils/injection_point.h" + +/* + * pg_get_injection_points + * + * Return a table of all the injection points currently attached to the + * system. + */ +Datum +pg_get_injection_points(PG_FUNCTION_ARGS) +{ +#define NUM_PG_GET_INJECTION_POINTS 3 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + List *inj_points; + ListCell *lc; + + /* Build a tuplestore to return our results in */ + InitMaterializedSRF(fcinfo, 0); + + inj_points = InjectionPointList(); + + foreach(lc, inj_points) + { + Datum values[NUM_PG_GET_INJECTION_POINTS]; + bool nulls[NUM_PG_GET_INJECTION_POINTS]; + InjectionPointData *inj_point = lfirst(lc); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + values[0] = PointerGetDatum(cstring_to_text(inj_point->name)); + values[1] = PointerGetDatum(cstring_to_text(inj_point->library)); + values[2] = PointerGetDatum(cstring_to_text(inj_point->function)); + + /* shove row into tuplestore */ + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + } + + return (Datum) 0; +#undef NUM_PG_GET_INJECTION_POINTS +} diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build index 9e389a00d057..4a89f1e016ea 100644 --- a/src/backend/utils/misc/meson.build +++ b/src/backend/utils/misc/meson.build @@ -7,6 +7,7 @@ backend_sources += files( 'guc_tables.c', 'help_config.c', 'injection_point.c', + 'injection_point_funcs.c', 'pg_config.c', 'pg_controldata.c', 'pg_rusage.c', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index d3d28a263fa9..554e03d2d68c 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12556,4 +12556,12 @@ proargnames => '{pid,io_id,io_generation,state,operation,off,length,target,handle_data_len,raw_result,result,target_desc,f_sync,f_localmem,f_buffered}', prosrc => 'pg_get_aios' }, +# Injection point functions +{ oid => '8490', descr => 'information about injection points attached', + proname => 'pg_get_injection_points', prorows => '3', proretset => 't', + provolatile => 'v', proparallel => 'r', prorettype => 'record', + proargtypes => '', proallargtypes => '{text,text,text}', + proargmodes => '{o,o,o}', proargnames => '{name,library,function}', + prosrc => 'pg_get_injection_points' }, + ] diff --git a/src/include/utils/injection_point.h b/src/include/utils/injection_point.h index a37958e1835f..fd5bc061b7bd 100644 --- a/src/include/utils/injection_point.h +++ b/src/include/utils/injection_point.h @@ -11,6 +11,19 @@ #ifndef INJECTION_POINT_H #define INJECTION_POINT_H +#include "nodes/pg_list.h" + +/* + * Injection point data, used when retrieving a list of all the attached + * injection points. + */ +typedef struct InjectionPointData +{ + const char *name; + const char *library; + const char *function; +} InjectionPointData; + /* * Injection points require --enable-injection-points. */ @@ -47,6 +60,9 @@ extern void InjectionPointCached(const char *name, void *arg); extern bool IsInjectionPointAttached(const char *name); extern bool InjectionPointDetach(const char *name); +/* Get the current set of injection points attached */ +extern List *InjectionPointList(void); + #ifdef EXEC_BACKEND extern PGDLLIMPORT struct InjectionPointsCtl *ActiveInjectionPoints; #endif diff --git a/src/test/modules/injection_points/expected/injection_points.out b/src/test/modules/injection_points/expected/injection_points.out index 43bcdd01582f..e59065029b41 100644 --- a/src/test/modules/injection_points/expected/injection_points.out +++ b/src/test/modules/injection_points/expected/injection_points.out @@ -39,6 +39,15 @@ SELECT injection_points_attach('TestInjectionLog2', 'notice'); (1 row) +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + name | library | function +--------------------+------------------+------------------ + TestInjectionError | injection_points | injection_error + TestInjectionLog | injection_points | injection_notice + TestInjectionLog2 | injection_points | injection_notice +(3 rows) + SELECT injection_points_run('TestInjectionBooh'); -- nothing injection_points_run ---------------------- @@ -298,5 +307,12 @@ SELECT injection_points_detach('TestConditionLocal1'); (1 row) +-- No points should be left around. +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + name | library | function +------+---------+---------- +(0 rows) + DROP EXTENSION injection_points; DROP FUNCTION wait_pid; diff --git a/src/test/modules/injection_points/sql/injection_points.sql b/src/test/modules/injection_points/sql/injection_points.sql index d9748331c771..2b5f28b73a60 100644 --- a/src/test/modules/injection_points/sql/injection_points.sql +++ b/src/test/modules/injection_points/sql/injection_points.sql @@ -18,6 +18,9 @@ SELECT injection_points_attach('TestInjectionError', 'error'); SELECT injection_points_attach('TestInjectionLog', 'notice'); SELECT injection_points_attach('TestInjectionLog2', 'notice'); +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + SELECT injection_points_run('TestInjectionBooh'); -- nothing SELECT injection_points_run('TestInjectionLog2'); -- notice SELECT injection_points_run('TestInjectionLog2', NULL); -- notice @@ -85,5 +88,9 @@ SELECT injection_points_detach('TestConditionError'); SELECT injection_points_attach('TestConditionLocal1', 'error'); SELECT injection_points_detach('TestConditionLocal1'); +-- No points should be left around. +SELECT name, library, function FROM pg_get_injection_points() + ORDER BY name COLLATE "C"; + DROP EXTENSION injection_points; DROP FUNCTION wait_pid; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index a8346cda633a..4ad6fdb0d003 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1283,6 +1283,7 @@ InjectionPointCacheEntry InjectionPointCallback InjectionPointCondition InjectionPointConditionType +InjectionPointData InjectionPointEntry InjectionPointsCtl InjectionPointSharedState