On Tue, Apr 15, 2025 at 04:37:51PM +0530, Rahila Syed wrote: > The function actually retrieves an array not a list, so the comment and > the function name may be misleading. > This function not only retrieves an array of injection points but also > the number of injection points. Would it be more efficient to define a > single > struct that includes both the array of injection points and the number of > points? > This way, both values can be returned from the function.
Right, I was wondering a bit what to do here. And I've come down to the conclusion to just make this API return a List. > Would it be useful to put the logic of the above function under #define > USE_INJECTION_POINT. This approach would make it possible to > distinguish between cases where no injection points are attached and > instances where the build does not support injection points. For this one, I've found that InjectionPointList() was the incorrect bit: we can make it issue an elog(ERROR) if !USE_INJECTION_POINTS. This way, none of its callers will be confused between the case of a NIL List meaning either !USE_INJECTION_POINTS or that there are no points attached if the build uses USE_INJECTION_POINTS. -- Michael
From 53447da561a03afa7e162c2bd478f3b76f984209 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Tue, 22 Apr 2025 12:59:30 +0900 Subject: [PATCH v4 1/2] Add InjectionPointList() to retrieve list of injection points This hides the internals of the shmem array lookup, allocating the result in a palloc'd array usable by the caller. --- src/include/utils/injection_point.h | 16 +++++++++ src/backend/utils/misc/injection_point.c | 46 ++++++++++++++++++++++++ src/tools/pgindent/typedefs.list | 1 + 3 files changed, 63 insertions(+) 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/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/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 -- 2.49.0
From 341cb1dfb8265697e2a2327ffc85399d940758ef Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Tue, 22 Apr 2025 13:13:27 +0900 Subject: [PATCH v4 2/2] Add pg_get_injection_points() This is a system function that displays the information about the injection points currently attached to the system, feeding from the states of things in shared memory. --- src/include/catalog/pg_proc.dat | 8 +++ src/backend/utils/misc/Makefile | 1 + .../utils/misc/injection_point_funcs.c | 60 +++++++++++++++++++ src/backend/utils/misc/meson.build | 1 + .../expected/injection_points.out | 16 +++++ .../injection_points/sql/injection_points.sql | 7 +++ src/test/regress/expected/misc_functions.out | 7 +++ src/test/regress/sql/misc_functions.sql | 3 + doc/src/sgml/func.sgml | 46 ++++++++++++++ 9 files changed, 149 insertions(+) create mode 100644 src/backend/utils/misc/injection_point_funcs.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/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_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/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/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 574a544d9fa4..297a53213887 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -28496,6 +28496,52 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres} </sect2> + <sect2 id="functions-info-injection-points"> + <title>Injection Points Information Functions</title> + + <para> + The functions shown in <xref linkend="functions-info-injection-points"/> + print information about the injection points. + See <xref linkend="xfunc-addin-injection-points" />. + </para> + + <table id="functions-injection-points"> + <title>WAL Summarization Information Functions</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="func_table_entry"><para role="func_signature"> + Function + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>pg_get_injection_points</primary> + </indexterm> + <function>pg_get_injection_points</function> () + <returnvalue>setof record</returnvalue> + ( <parameter>name</parameter> <type>text</type>, + <parameter>library</parameter> <type>text</type>, + <parameter>function</parameter> <type>text</type> ) + </para> + <para> + Returns information about the injection points currently attached + to the cluster. + </para></entry> + </row> + </tbody> + + </tgroup> + </table> + </sect2> + </sect1> <sect1 id="functions-admin"> -- 2.49.0
signature.asc
Description: PGP signature