public inbox for [email protected]  
help / color / mirror / Atom feed
From: Andrei Lepikhov <[email protected]>
To: PostgreSQL-development <[email protected]>
Subject: Add Postgres module info
Date: Wed, 11 Dec 2024 15:35:43 +0700
Message-ID: <[email protected]> (raw)

Hi,

I would like to propose the module_info structure, which aims to let 
extension maintainers sew some data into the binary file. Being included 
in the module code, this information remains unchanged and is available 
for reading by a backend.
As I see, this question was already debated during the introduction of 
PG_MODULE_MAGIC [1], and developers didn't add such data because the 
practical case wasn't obvious. Right now, when multiple extensions are 
maintained and used in installations constantly, we have enough 
information to continue this discussion.
The idea was initially born with the support of the extension, which had 
a stable UI and frequently changing library code. To answer customer 
requests, it was necessary to know the specific version of the code to 
reproduce the situation in a test environment. That required introducing 
a file naming rule and a specific exported variable into the module 
code. However, this is not a sufficient guarantee of version 
determination and complicates the technical process when supporting many 
extensions obtained from different sources.
Another example is a library without an installation script at all - see 
auto_explain. Without a 'CREATE EXTENSION' call, we don't have an option 
to find out the library's version.
It would be much easier if the Postgres catalogue contained a function, 
for example, module_info(module_name), which would allow you to 
determine the file's full path and name containing the desired module in 
the psql console and its version.
On the other hand, the Omnigres project (author Yurii Rashkovski) also 
came up with the idea of ​​​​module versioning, although it does this 
externally out of the Postgres core. When designing this code, I also 
adopted ideas from this repository.
So, let me propose a patch that introduces this tiny feature: the 
maintainer can add the PG_MODULE_INFO macro to the library code, and 
Postgres reads it on the module's load.

There is a question of how much information makes sense to add to the 
module. For now, each time I prepare extensions to release, I have to 
add the extension name (to avoid issues with file naming/renaming) and 
the version. Format of the version storage? Do we need a separate minor 
version number? It is a subject to debate.

[1] 
https://www.postgresql.org/message-id/flat/20060507211705.GB3808%40svana.org

-- 
regards, Andrei Lepikhov

From 56d4a0dadd32a601d52ed59b38935a36a175f635 Mon Sep 17 00:00:00 2001
From: "Andrei V. Lepikhov" <[email protected]>
Date: Tue, 19 Nov 2024 18:45:36 +0700
Subject: [PATCH v0] Introduce MODULE_INFO macro.

This optional macro allows dynamically loaded shared libraries (modules)
a standard way to incorporate version and name data. The introduced catalogue
routine module_info can be used to find this module by name and check
the version. It makes users independent from file naming conventions.

With a growing number of Postgres core hooks and the introduction of named DSM
segments, the number of modules that don't need to be loaded on startup may
grow fast. Moreover, in many cases related to query tree transformation or
extra path recommendation, such modules might not need database objects except
GUCs - see auto_explain as an example. That means they don't need to execute
the 'CREATE EXTENSION' statement at all and don't have a record in
the pg_extension table. Such a trick provides much flexibility, including
an online upgrade and may become widespread.

In addition, it is also convenient in support to be sure that the installation
(or at least the backend) includes a specific version of the module. Even if
a module has an installation script, it is not rare that it provides
an implementation for a range of UI routine versions. It makes sense to ensure
which specific version of the code is used.

Discussions [1,2] already mentioned module-info stuff, but at that time,
extensibility techniques and extension popularity were low, and it wasn't
necessary to provide that data.

[1] https://www.postgresql.org/message-id/flat/20060507211705.GB3808%40svana.org
[2] https://www.postgresql.org/message-id/flat/20051106162658.34c31d57%40thunder.logicalchaos.org
---
 contrib/auto_explain/auto_explain.c           |  2 +
 contrib/auto_explain/t/001_auto_explain.pl    |  9 +++
 contrib/pg_prewarm/t/001_basic.pl             | 10 ++-
 .../pg_stat_statements/pg_stat_statements.c   |  1 +
 src/backend/utils/fmgr/dfmgr.c                | 73 ++++++++++++++++++-
 src/include/catalog/pg_proc.dat               |  6 ++
 src/include/fmgr.h                            | 18 +++++
 7 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 623a674f99..f4110eb1aa 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -22,6 +22,8 @@
 
 PG_MODULE_MAGIC;
 
+PG_MODULE_INFO("auto_explain", 1000000);
+
 /* GUC variables */
 static int	auto_explain_log_min_duration = -1; /* msec or -1 */
 static int	auto_explain_log_parameter_max_length = -1; /* bytes or -1 */
diff --git a/contrib/auto_explain/t/001_auto_explain.pl b/contrib/auto_explain/t/001_auto_explain.pl
index 0e5b34afa9..d60c2cbbf1 100644
--- a/contrib/auto_explain/t/001_auto_explain.pl
+++ b/contrib/auto_explain/t/001_auto_explain.pl
@@ -53,6 +53,15 @@ like(
 	qr/Seq Scan on pg_class/,
 	"sequential scan logged, text mode");
 
+$log_contents = $node->safe_psql(
+	"postgres", q{SELECT substring(libname, 'auto_explain'),version
+				  FROM module_info('auto_explain');});
+
+like(
+	$log_contents,
+	qr/auto_explain|1000000/,
+	"Check module version");
+
 # Prepared query.
 $log_contents = query_log($node,
 	q{PREPARE get_proc(name) AS SELECT * FROM pg_proc WHERE proname = $1; EXECUTE get_proc('int4pl');}
diff --git a/contrib/pg_prewarm/t/001_basic.pl b/contrib/pg_prewarm/t/001_basic.pl
index 825d3448ee..141b8b7d7d 100644
--- a/contrib/pg_prewarm/t/001_basic.pl
+++ b/contrib/pg_prewarm/t/001_basic.pl
@@ -25,8 +25,16 @@ $node->safe_psql("postgres",
 	  . "CREATE TABLE test(c1 int);\n"
 	  . "INSERT INTO test SELECT generate_series(1, 100);");
 
-# test read mode
+# test empty module info
 my $result =
+  $node->safe_psql("postgres", "SELECT * FROM module_info('fake_module');");
+like($result, qr/|/, 'Return null if module does not exist');
+$result =
+  $node->safe_psql("postgres", "SELECT * FROM module_info('pg_prewarm');");
+like($result, qr/|/, 'Return null if module does not provide an info');
+
+# test read mode
+$result =
   $node->safe_psql("postgres", "SELECT pg_prewarm('test', 'read');");
 like($result, qr/^[1-9][0-9]*$/, 'read mode succeeded');
 
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 49c657b3e0..0afd23211a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -72,6 +72,7 @@
 #include "utils/timestamp.h"
 
 PG_MODULE_MAGIC;
+PG_MODULE_INFO("pg_stat_statements", 010000);
 
 /* Location of permanent stats file (valid when database is shut down) */
 #define PGSS_DUMP_FILE	PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index c7aa789b51..5931c30502 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -31,10 +31,12 @@
 #endif							/* !WIN32 */
 
 #include "fmgr.h"
+#include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "storage/shmem.h"
+#include "utils/builtins.h"
 #include "utils/hsearch.h"
 
 
@@ -61,7 +63,9 @@ typedef struct df_files
 	ino_t		inode;			/* Inode number of file */
 #endif
 	void	   *handle;			/* a handle for pg_dl* functions */
-	char		filename[FLEXIBLE_ARRAY_MEMBER];	/* Full pathname of file */
+	char		name[NAMEDATALEN];
+	int			version;
+	char		filename[256];	/* Full pathname of file */
 } DynamicFileList;
 
 static DynamicFileList *file_list = NULL;
@@ -185,6 +189,7 @@ internal_load_library(const char *libname)
 {
 	DynamicFileList *file_scanner;
 	PGModuleMagicFunction magic_func;
+	PGModuleInfoFunction minfo_func;
 	char	   *load_error;
 	struct stat stat_buf;
 	PG_init_t	PG_init;
@@ -281,6 +286,26 @@ internal_load_library(const char *libname)
 					 errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
 		}
 
+		/*
+		 * Module info is an optional block. The PG_MODULE_MAGIC machinery
+		 * already checked compatibility of the module and the core codes. So,
+		 * we doesn't need to be too careful here.
+		 */
+		minfo_func = (PGModuleInfoFunction)
+			dlsym(file_scanner->handle, PG_MODULEINFO_FUNCTION_NAME_STRING);
+		if (minfo_func)
+		{
+			const pg_minfo_struct *minfo = (*minfo_func) ();
+
+			file_scanner->version = minfo->ver;
+			strcpy(file_scanner->name, minfo->name);
+		}
+		else
+		{
+			/* That means the library doesn't provide module info so far. */
+			file_scanner->version = -1;
+		}
+
 		/*
 		 * If the library has a _PG_init() function, call it.
 		 */
@@ -685,3 +710,49 @@ RestoreLibraryState(char *start_address)
 		start_address += strlen(start_address) + 1;
 	}
 }
+
+Datum
+module_info(PG_FUNCTION_ARGS)
+{
+	FuncCallContext	   *funcctx;
+	MemoryContext		oldcontext;
+	char			   *module_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	DynamicFileList	   *file_scanner;
+	TupleDesc			tupdesc;
+	List			   *modlst = NIL;
+	Datum				result;
+	Datum				values[2];
+	bool				isnull[2] = {0,0};
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+			elog(ERROR, "return type must be a row type");
+
+		for (file_scanner = file_list; file_scanner != NULL;
+			file_scanner = file_scanner->next)
+		{
+			if (strcmp(module_name, file_scanner->name) == 0)
+				modlst = lappend(modlst, file_scanner);
+		}
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+
+	if (modlst != NIL)
+	{
+		file_scanner = (DynamicFileList *) llast(modlst);
+		values[0] = CStringGetTextDatum(file_scanner->filename);
+		values[1] = Int32GetDatum(file_scanner->version);
+		result = HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+		modlst = list_delete_last(modlst);
+		SRF_RETURN_NEXT(funcctx, result);
+	}
+	else
+		SRF_RETURN_DONE(funcctx);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cbbe8acd38..9f37c4fc1e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -263,6 +263,12 @@
 { oid => '89', descr => 'PostgreSQL version string',
   proname => 'version', provolatile => 's', prorettype => 'text',
   proargtypes => '', prosrc => 'pgsql_version' },
+{ oid => '111', descr => 'Module Info',
+  proname => 'module_info', provolatile => 's', prorettype => 'record',
+  proretset => 't', proargtypes => 'text', proallargtypes => '{text,text,int4}',
+  proargmodes => '{i,o,o}',
+  proargnames => '{module_name,libname,version}',
+  prosrc => 'module_info' },
 
 { oid => '86', descr => 'I/O',
   proname => 'pg_ddl_command_in', prorettype => 'pg_ddl_command',
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 1e3795de4a..21b186a17e 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -483,6 +483,12 @@ typedef struct
 	FMGR_ABI_EXTRA, \
 }
 
+typedef struct pg_minfo_struct
+{
+	char	name[NAMEDATALEN];
+	int32	ver;
+} pg_minfo_struct;
+
 StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_magic_struct *) 0)->abi_extra),
 				 "FMGR_ABI_EXTRA too long");
 
@@ -492,8 +498,12 @@ StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_magic_struct *) 0)->abi_e
  */
 typedef const Pg_magic_struct *(*PGModuleMagicFunction) (void);
 
+typedef const pg_minfo_struct *(*PGModuleInfoFunction) (void);
+
 #define PG_MAGIC_FUNCTION_NAME Pg_magic_func
 #define PG_MAGIC_FUNCTION_NAME_STRING "Pg_magic_func"
+#define PG_MODULEINFO_FUNCTION_NAME pg_module_info
+#define PG_MODULEINFO_FUNCTION_NAME_STRING "pg_module_info"
 
 #define PG_MODULE_MAGIC \
 extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
@@ -505,6 +515,14 @@ PG_MAGIC_FUNCTION_NAME(void) \
 } \
 extern int no_such_variable
 
+#define PG_MODULE_INFO(modulename, version) \
+extern PGDLLEXPORT const pg_minfo_struct *PG_MODULEINFO_FUNCTION_NAME(void); \
+const pg_minfo_struct * \
+PG_MODULEINFO_FUNCTION_NAME(void) \
+{ \
+	static const pg_minfo_struct module_info = {modulename, version}; \
+	return &module_info; \
+} \
 
 /*-------------------------------------------------------------------------
  *		Support routines and macros for callers of fmgr-compatible functions
-- 
2.47.1



Attachments:

  [text/plain] v0-0001-Introduce-MODULE_INFO-macro.patch (10.1K, ../[email protected]/2-v0-0001-Introduce-MODULE_INFO-macro.patch)
  download | inline diff:
From 56d4a0dadd32a601d52ed59b38935a36a175f635 Mon Sep 17 00:00:00 2001
From: "Andrei V. Lepikhov" <[email protected]>
Date: Tue, 19 Nov 2024 18:45:36 +0700
Subject: [PATCH v0] Introduce MODULE_INFO macro.

This optional macro allows dynamically loaded shared libraries (modules)
a standard way to incorporate version and name data. The introduced catalogue
routine module_info can be used to find this module by name and check
the version. It makes users independent from file naming conventions.

With a growing number of Postgres core hooks and the introduction of named DSM
segments, the number of modules that don't need to be loaded on startup may
grow fast. Moreover, in many cases related to query tree transformation or
extra path recommendation, such modules might not need database objects except
GUCs - see auto_explain as an example. That means they don't need to execute
the 'CREATE EXTENSION' statement at all and don't have a record in
the pg_extension table. Such a trick provides much flexibility, including
an online upgrade and may become widespread.

In addition, it is also convenient in support to be sure that the installation
(or at least the backend) includes a specific version of the module. Even if
a module has an installation script, it is not rare that it provides
an implementation for a range of UI routine versions. It makes sense to ensure
which specific version of the code is used.

Discussions [1,2] already mentioned module-info stuff, but at that time,
extensibility techniques and extension popularity were low, and it wasn't
necessary to provide that data.

[1] https://www.postgresql.org/message-id/flat/20060507211705.GB3808%40svana.org
[2] https://www.postgresql.org/message-id/flat/20051106162658.34c31d57%40thunder.logicalchaos.org
---
 contrib/auto_explain/auto_explain.c           |  2 +
 contrib/auto_explain/t/001_auto_explain.pl    |  9 +++
 contrib/pg_prewarm/t/001_basic.pl             | 10 ++-
 .../pg_stat_statements/pg_stat_statements.c   |  1 +
 src/backend/utils/fmgr/dfmgr.c                | 73 ++++++++++++++++++-
 src/include/catalog/pg_proc.dat               |  6 ++
 src/include/fmgr.h                            | 18 +++++
 7 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c
index 623a674f99..f4110eb1aa 100644
--- a/contrib/auto_explain/auto_explain.c
+++ b/contrib/auto_explain/auto_explain.c
@@ -22,6 +22,8 @@
 
 PG_MODULE_MAGIC;
 
+PG_MODULE_INFO("auto_explain", 1000000);
+
 /* GUC variables */
 static int	auto_explain_log_min_duration = -1; /* msec or -1 */
 static int	auto_explain_log_parameter_max_length = -1; /* bytes or -1 */
diff --git a/contrib/auto_explain/t/001_auto_explain.pl b/contrib/auto_explain/t/001_auto_explain.pl
index 0e5b34afa9..d60c2cbbf1 100644
--- a/contrib/auto_explain/t/001_auto_explain.pl
+++ b/contrib/auto_explain/t/001_auto_explain.pl
@@ -53,6 +53,15 @@ like(
 	qr/Seq Scan on pg_class/,
 	"sequential scan logged, text mode");
 
+$log_contents = $node->safe_psql(
+	"postgres", q{SELECT substring(libname, 'auto_explain'),version
+				  FROM module_info('auto_explain');});
+
+like(
+	$log_contents,
+	qr/auto_explain|1000000/,
+	"Check module version");
+
 # Prepared query.
 $log_contents = query_log($node,
 	q{PREPARE get_proc(name) AS SELECT * FROM pg_proc WHERE proname = $1; EXECUTE get_proc('int4pl');}
diff --git a/contrib/pg_prewarm/t/001_basic.pl b/contrib/pg_prewarm/t/001_basic.pl
index 825d3448ee..141b8b7d7d 100644
--- a/contrib/pg_prewarm/t/001_basic.pl
+++ b/contrib/pg_prewarm/t/001_basic.pl
@@ -25,8 +25,16 @@ $node->safe_psql("postgres",
 	  . "CREATE TABLE test(c1 int);\n"
 	  . "INSERT INTO test SELECT generate_series(1, 100);");
 
-# test read mode
+# test empty module info
 my $result =
+  $node->safe_psql("postgres", "SELECT * FROM module_info('fake_module');");
+like($result, qr/|/, 'Return null if module does not exist');
+$result =
+  $node->safe_psql("postgres", "SELECT * FROM module_info('pg_prewarm');");
+like($result, qr/|/, 'Return null if module does not provide an info');
+
+# test read mode
+$result =
   $node->safe_psql("postgres", "SELECT pg_prewarm('test', 'read');");
 like($result, qr/^[1-9][0-9]*$/, 'read mode succeeded');
 
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 49c657b3e0..0afd23211a 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -72,6 +72,7 @@
 #include "utils/timestamp.h"
 
 PG_MODULE_MAGIC;
+PG_MODULE_INFO("pg_stat_statements", 010000);
 
 /* Location of permanent stats file (valid when database is shut down) */
 #define PGSS_DUMP_FILE	PGSTAT_STAT_PERMANENT_DIRECTORY "/pg_stat_statements.stat"
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
index c7aa789b51..5931c30502 100644
--- a/src/backend/utils/fmgr/dfmgr.c
+++ b/src/backend/utils/fmgr/dfmgr.c
@@ -31,10 +31,12 @@
 #endif							/* !WIN32 */
 
 #include "fmgr.h"
+#include "funcapi.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "storage/shmem.h"
+#include "utils/builtins.h"
 #include "utils/hsearch.h"
 
 
@@ -61,7 +63,9 @@ typedef struct df_files
 	ino_t		inode;			/* Inode number of file */
 #endif
 	void	   *handle;			/* a handle for pg_dl* functions */
-	char		filename[FLEXIBLE_ARRAY_MEMBER];	/* Full pathname of file */
+	char		name[NAMEDATALEN];
+	int			version;
+	char		filename[256];	/* Full pathname of file */
 } DynamicFileList;
 
 static DynamicFileList *file_list = NULL;
@@ -185,6 +189,7 @@ internal_load_library(const char *libname)
 {
 	DynamicFileList *file_scanner;
 	PGModuleMagicFunction magic_func;
+	PGModuleInfoFunction minfo_func;
 	char	   *load_error;
 	struct stat stat_buf;
 	PG_init_t	PG_init;
@@ -281,6 +286,26 @@ internal_load_library(const char *libname)
 					 errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
 		}
 
+		/*
+		 * Module info is an optional block. The PG_MODULE_MAGIC machinery
+		 * already checked compatibility of the module and the core codes. So,
+		 * we doesn't need to be too careful here.
+		 */
+		minfo_func = (PGModuleInfoFunction)
+			dlsym(file_scanner->handle, PG_MODULEINFO_FUNCTION_NAME_STRING);
+		if (minfo_func)
+		{
+			const pg_minfo_struct *minfo = (*minfo_func) ();
+
+			file_scanner->version = minfo->ver;
+			strcpy(file_scanner->name, minfo->name);
+		}
+		else
+		{
+			/* That means the library doesn't provide module info so far. */
+			file_scanner->version = -1;
+		}
+
 		/*
 		 * If the library has a _PG_init() function, call it.
 		 */
@@ -685,3 +710,49 @@ RestoreLibraryState(char *start_address)
 		start_address += strlen(start_address) + 1;
 	}
 }
+
+Datum
+module_info(PG_FUNCTION_ARGS)
+{
+	FuncCallContext	   *funcctx;
+	MemoryContext		oldcontext;
+	char			   *module_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+	DynamicFileList	   *file_scanner;
+	TupleDesc			tupdesc;
+	List			   *modlst = NIL;
+	Datum				result;
+	Datum				values[2];
+	bool				isnull[2] = {0,0};
+
+	if (SRF_IS_FIRSTCALL())
+	{
+		funcctx = SRF_FIRSTCALL_INIT();
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+			elog(ERROR, "return type must be a row type");
+
+		for (file_scanner = file_list; file_scanner != NULL;
+			file_scanner = file_scanner->next)
+		{
+			if (strcmp(module_name, file_scanner->name) == 0)
+				modlst = lappend(modlst, file_scanner);
+		}
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	funcctx = SRF_PERCALL_SETUP();
+
+	if (modlst != NIL)
+	{
+		file_scanner = (DynamicFileList *) llast(modlst);
+		values[0] = CStringGetTextDatum(file_scanner->filename);
+		values[1] = Int32GetDatum(file_scanner->version);
+		result = HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
+		modlst = list_delete_last(modlst);
+		SRF_RETURN_NEXT(funcctx, result);
+	}
+	else
+		SRF_RETURN_DONE(funcctx);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index cbbe8acd38..9f37c4fc1e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -263,6 +263,12 @@
 { oid => '89', descr => 'PostgreSQL version string',
   proname => 'version', provolatile => 's', prorettype => 'text',
   proargtypes => '', prosrc => 'pgsql_version' },
+{ oid => '111', descr => 'Module Info',
+  proname => 'module_info', provolatile => 's', prorettype => 'record',
+  proretset => 't', proargtypes => 'text', proallargtypes => '{text,text,int4}',
+  proargmodes => '{i,o,o}',
+  proargnames => '{module_name,libname,version}',
+  prosrc => 'module_info' },
 
 { oid => '86', descr => 'I/O',
   proname => 'pg_ddl_command_in', prorettype => 'pg_ddl_command',
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 1e3795de4a..21b186a17e 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -483,6 +483,12 @@ typedef struct
 	FMGR_ABI_EXTRA, \
 }
 
+typedef struct pg_minfo_struct
+{
+	char	name[NAMEDATALEN];
+	int32	ver;
+} pg_minfo_struct;
+
 StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_magic_struct *) 0)->abi_extra),
 				 "FMGR_ABI_EXTRA too long");
 
@@ -492,8 +498,12 @@ StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_magic_struct *) 0)->abi_e
  */
 typedef const Pg_magic_struct *(*PGModuleMagicFunction) (void);
 
+typedef const pg_minfo_struct *(*PGModuleInfoFunction) (void);
+
 #define PG_MAGIC_FUNCTION_NAME Pg_magic_func
 #define PG_MAGIC_FUNCTION_NAME_STRING "Pg_magic_func"
+#define PG_MODULEINFO_FUNCTION_NAME pg_module_info
+#define PG_MODULEINFO_FUNCTION_NAME_STRING "pg_module_info"
 
 #define PG_MODULE_MAGIC \
 extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
@@ -505,6 +515,14 @@ PG_MAGIC_FUNCTION_NAME(void) \
 } \
 extern int no_such_variable
 
+#define PG_MODULE_INFO(modulename, version) \
+extern PGDLLEXPORT const pg_minfo_struct *PG_MODULEINFO_FUNCTION_NAME(void); \
+const pg_minfo_struct * \
+PG_MODULEINFO_FUNCTION_NAME(void) \
+{ \
+	static const pg_minfo_struct module_info = {modulename, version}; \
+	return &module_info; \
+} \
 
 /*-------------------------------------------------------------------------
  *		Support routines and macros for callers of fmgr-compatible functions
-- 
2.47.1



view thread (2+ messages)

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected]
  Subject: Re: Add Postgres module info
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox