On Mon, Apr 14, 2025 at 06:15:07AM +0000, Hayato Kuroda (Fujitsu) wrote:
> Apart from functions related with injection_points, this can be called even 
> when
> postgres has been built with -Dinjection_points=false. This is intentional 
> because
> this function does not have any side-effect and just refers the status. Is my
> understanding correct?

Yes.  The function could be changed to return an ERROR if the build
does not support this option.  It's more portable to return NULL if
you have some queries that do joins.  This could be used with
pg_stat_activity and wait events for example, and some points are in
positions strategic enough that they could be used across more than
one library, like the restart point one or the autovacuum startup one.

> I'm not sure it is directly related, but ISTM there are no direct ways to 
> check
> whether the injection_points is enabled or not. How do you think adding the
> function?

Yeah, we could use something like that, not sure if that's worth it.

> Regarding the internal of the patch, it could be crashed when two points are
> attached and then first one is detached [1]. I think we should not use "idx" 
> for
> the result array - PSA the fix.

Oops.  That's a brain fart from my side.  Thanks.
--
Michael
From ee033a0f883d52638cc9c90b9c0eff29b9dde5a5 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Tue, 15 Apr 2025 08:39:36 +0900
Subject: [PATCH v2] 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/include/utils/injection_point.h           | 14 +++++
 src/backend/utils/misc/Makefile               |  1 +
 src/backend/utils/misc/injection_point.c      | 53 +++++++++++++++++
 .../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 +++++++++++++++
 src/tools/pgindent/typedefs.list              |  1 +
 12 files changed, 216 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/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/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..ea7e6dba5211 100644
--- a/src/backend/utils/misc/injection_point.c
+++ b/src/backend/utils/misc/injection_point.c
@@ -584,3 +584,56 @@ 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 (int 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/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">
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

Attachment: signature.asc
Description: PGP signature

Reply via email to