On Tue, Apr 15, 2025 at 02:49:24AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Should cur_pos be uint32 instead of int? Either of them can work because
> cur_pos can be [0, 128], but it may be clearer.
> 
> Apart from above, LGTM.

Sure, why not.  Thanks for the review.

An important thing may be to split the change into two:
- First one for the API in injection_point.c.
- Second one for the new function.

The second patch is still something folks seem to be meh about, but if
the end consensus is to put the new SQL function in the module
injection_points we are going to need the first patch anyway.
--
Michael
From d738943616f1af8fc2ce1d309a3b7a0b77f29a22 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 15 Apr 2025 14:40:30 +0900
Subject: [PATCH v3 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      | 14 +++++++
 src/backend/utils/misc/injection_point.c | 52 ++++++++++++++++++++++++
 src/tools/pgindent/typedefs.list         |  1 +
 3 files changed, 67 insertions(+)

diff --git a/src/include/utils/injection_point.h 
b/src/include/utils/injection_point.h
index 6ba64cd1ebc6..c5c78914b848 100644
--- a/src/include/utils/injection_point.h
+++ b/src/include/utils/injection_point.h
@@ -11,6 +11,17 @@
 #ifndef INJECTION_POINT_H
 #define INJECTION_POINT_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 +57,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 InjectionPointData *InjectionPointList(uint32 *num_points);
+
 #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..1f76f25aafb9 100644
--- a/src/backend/utils/misc/injection_point.c
+++ b/src/backend/utils/misc/injection_point.c
@@ -584,3 +584,55 @@ 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.
+ */
+InjectionPointData *
+InjectionPointList(uint32 *num_points)
+{
+#ifdef USE_INJECTION_POINTS
+       InjectionPointData *result;
+       uint32          max_inuse;
+       int                     cur_pos = 0;
+
+       LWLockAcquire(InjectionPointLock, LW_SHARED);
+
+       max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
+
+       /*
+        * This overestimates the allocation size, including slots that may be
+        * free, for simplicity.
+        */
+       result = palloc0(sizeof(InjectionPointData) * max_inuse);
+
+       for (uint32 idx = 0; idx < max_inuse; idx++)
+       {
+               InjectionPointEntry *entry;
+               uint64          generation;
+
+               entry = &ActiveInjectionPoints->entries[idx];
+               generation = pg_atomic_read_u64(&entry->generation);
+
+               /* skip free slots */
+               if (generation % 2 == 0)
+                       continue;
+
+               result[cur_pos].name = pstrdup(entry->name);
+               result[cur_pos].library = pstrdup(entry->library);
+               result[cur_pos].function = pstrdup(entry->function);
+               cur_pos++;
+       }
+
+       LWLockRelease(InjectionPointLock);
+
+       *num_points = cur_pos;
+       return result;
+
+#else
+       *num_points = 0;
+       return NULL;
+#endif
+}
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d16bc208654b..e7df2dc10684 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1281,6 +1281,7 @@ InjectionPointCacheEntry
 InjectionPointCallback
 InjectionPointCondition
 InjectionPointConditionType
+InjectionPointData
 InjectionPointEntry
 InjectionPointsCtl
 InjectionPointSharedState
-- 
2.49.0

From 0e0fb1cfbc2ab863f5e3ff38acd6a29ba1656161 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 15 Apr 2025 14:40:55 +0900
Subject: [PATCH v3 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        | 59 +++++++++++++++++++
 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, 148 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..750ac4abf67d
--- /dev/null
+++ b/src/backend/utils/misc/injection_point_funcs.c
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+       uint32          num_points = 0;
+       InjectionPointData *injps = NULL;
+
+       /* Build a tuplestore to return our results in */
+       InitMaterializedSRF(fcinfo, 0);
+
+       injps = InjectionPointList(&num_points);
+
+       for (uint32 idx = 0; idx < num_points; idx++)
+       {
+               Datum           values[NUM_PG_GET_INJECTION_POINTS];
+               bool            nulls[NUM_PG_GET_INJECTION_POINTS];
+
+               memset(values, 0, sizeof(values));
+               memset(nulls, 0, sizeof(nulls));
+
+               values[0] = PointerGetDatum(cstring_to_text(injps[idx].name));
+               values[1] = 
PointerGetDatum(cstring_to_text(injps[idx].library));
+               values[2] = 
PointerGetDatum(cstring_to_text(injps[idx].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 1c5cfee25d12..6c0e0b45fa83 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

Attachment: signature.asc
Description: PGP signature

Reply via email to