diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index af3d056b9923..61f20ce8d5ae 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -28496,6 +28496,52 @@ 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. + + + + + +
+
+ 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 97ab851f0a63..60b406e4c2e0 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 62beb71da288..d087d9fda1e8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12566,4 +12566,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 6ba64cd1ebc6..3714739a8772 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. */ @@ -46,6 +59,9 @@ extern void InjectionPointCached(const char *name); 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 f25bbe4966ee..4b1436a3ef69 100644 --- a/src/test/modules/injection_points/expected/injection_points.out +++ b/src/test/modules/injection_points/expected/injection_points.out @@ -26,6 +26,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 ---------------------- @@ -253,5 +262,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 e3a481d60449..b12c40551954 100644 --- a/src/test/modules/injection_points/sql/injection_points.sql +++ b/src/test/modules/injection_points/sql/injection_points.sql @@ -14,6 +14,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('TestInjectionLog'); -- notice @@ -75,5 +78,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/test/regress/expected/misc_functions.out b/src/test/regress/expected/misc_functions.out index d3f5d16a6725..c09bff495045 100644 --- a/src/test/regress/expected/misc_functions.out +++ b/src/test/regress/expected/misc_functions.out @@ -802,6 +802,13 @@ SELECT count(*) > 0 AS ok FROM pg_control_system(); t (1 row) +-- Test function for injection point +SELECT count(*) >= 0 AS ok FROM pg_get_injection_points(); + ok +---- + t +(1 row) + -- pg_split_walfile_name, pg_walfile_name & pg_walfile_name_offset SELECT * FROM pg_split_walfile_name(NULL); segment_number | timeline_id diff --git a/src/test/regress/sql/misc_functions.sql b/src/test/regress/sql/misc_functions.sql index aaebb298330b..e351a19a8744 100644 --- a/src/test/regress/sql/misc_functions.sql +++ b/src/test/regress/sql/misc_functions.sql @@ -355,6 +355,9 @@ SELECT count(*) > 0 AS ok FROM pg_control_init(); SELECT count(*) > 0 AS ok FROM pg_control_recovery(); SELECT count(*) > 0 AS ok FROM pg_control_system(); +-- Test function for injection point +SELECT count(*) >= 0 AS ok FROM pg_get_injection_points(); + -- pg_split_walfile_name, pg_walfile_name & pg_walfile_name_offset SELECT * FROM pg_split_walfile_name(NULL); SELECT * FROM pg_split_walfile_name('invalid'); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index e5879e00dffe..31d5f8f71f46 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1282,6 +1282,7 @@ InjectionPointCacheEntry InjectionPointCallback InjectionPointCondition InjectionPointConditionType +InjectionPointData InjectionPointEntry InjectionPointsCtl InjectionPointSharedState