Skip to content

Commit 9324c8c

Browse files
committed
Introduce PG_MODULE_MAGIC_EXT macro.
This macro allows dynamically loaded shared libraries (modules) to provide a wired-in module name and version, and possibly other compile-time-constant fields in future. This information can be retrieved with the new pg_get_loaded_modules() function. This feature is expected to be particularly useful for modules that do not have any exposed SQL functionality and thus are not associated with a SQL-level extension object. But even for modules that do belong to extensions, being able to verify the actual code version can be useful. Author: Andrei Lepikhov <lepihov@gmail.com> Reviewed-by: Yurii Rashkovskii <yrashk@omnigres.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/dd4d1b59-d0fe-49d5-b28f-1e463b68fa32@gmail.com
1 parent e92c063 commit 9324c8c

File tree

10 files changed

+248
-28
lines changed

10 files changed

+248
-28
lines changed

contrib/auto_explain/auto_explain.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222
#include "executor/instrument.h"
2323
#include "utils/guc.h"
2424

25-
PG_MODULE_MAGIC;
25+
PG_MODULE_MAGIC_EXT(
26+
.name = "auto_explain",
27+
.version = PG_VERSION
28+
);
2629

2730
/* GUC variables */
2831
static int auto_explain_log_min_duration = -1; /* msec or -1 */

contrib/auto_explain/t/001_auto_explain.pl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,17 @@ sub query_log
212212
DROP USER regress_user1;
213213
});
214214

215+
# Test pg_get_loaded_modules() function. This function is particularly
216+
# useful for modules with no SQL presence, such as auto_explain.
217+
218+
my $res = $node->safe_psql(
219+
"postgres", q{
220+
SELECT module_name,
221+
version = current_setting('server_version') as version_ok,
222+
regexp_replace(file_name, '\..*', '') as file_name_stripped
223+
FROM pg_get_loaded_modules()
224+
WHERE module_name = 'auto_explain';
225+
});
226+
like($res, qr/^auto_explain\|t\|auto_explain$/, "pg_get_loaded_modules() ok");
227+
215228
done_testing();

doc/src/sgml/func.sgml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25040,6 +25040,28 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
2504025040
</para></entry>
2504125041
</row>
2504225042

25043+
<row>
25044+
<entry role="func_table_entry"><para role="func_signature">
25045+
<indexterm>
25046+
<primary>pg_get_loaded_modules</primary>
25047+
</indexterm>
25048+
<function>pg_get_loaded_modules</function> ()
25049+
<returnvalue>setof record</returnvalue>
25050+
( <parameter>module_name</parameter> <type>text</type>,
25051+
<parameter>version</parameter> <type>text</type>,
25052+
<parameter>file_name</parameter> <type>text</type> )
25053+
</para>
25054+
<para>
25055+
Returns a list of the loadable modules that are loaded into the
25056+
current server session. The <parameter>module_name</parameter>
25057+
and <parameter>version</parameter> fields are NULL unless the
25058+
module author supplied values for them using
25059+
the <literal>PG_MODULE_MAGIC_EXT</literal> macro.
25060+
The <parameter>file_name</parameter> field gives the file
25061+
name of the module (shared library).
25062+
</para></entry>
25063+
</row>
25064+
2504325065
<row>
2504425066
<entry role="func_table_entry"><para role="func_signature">
2504525067
<indexterm>

doc/src/sgml/xfunc.sgml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,9 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision
19541954
<indexterm zone="xfunc-c-dynload">
19551955
<primary>magic block</primary>
19561956
</indexterm>
1957+
<indexterm zone="xfunc-c-dynload">
1958+
<primary><literal>PG_MODULE_MAGIC</literal></primary>
1959+
</indexterm>
19571960

19581961
<para>
19591962
To ensure that a dynamically loaded object file is not loaded into an
@@ -1968,6 +1971,30 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision
19681971
<programlisting>
19691972
PG_MODULE_MAGIC;
19701973
</programlisting>
1974+
or
1975+
<programlisting>
1976+
PG_MODULE_MAGIC_EXT(<replaceable>parameters</replaceable>);
1977+
</programlisting>
1978+
</para>
1979+
1980+
<para>
1981+
The <literal>PG_MODULE_MAGIC_EXT</literal> variant allows the specification
1982+
of additional information about the module; currently, a name and/or a
1983+
version string can be added. (More fields might be allowed in future.)
1984+
Write something like this:
1985+
1986+
<programlisting>
1987+
PG_MODULE_MAGIC_EXT(
1988+
.name = "my_module_name",
1989+
.version = "1.2.3"
1990+
);
1991+
</programlisting>
1992+
1993+
Subsequently the name and version can be examined via
1994+
the <function>pg_get_loaded_modules()</function> function.
1995+
The meaning of the version string is not restricted
1996+
by <productname>PostgreSQL</productname>, but use of semantic versioning
1997+
rules is recommended.
19711998
</para>
19721999

19732000
<para>

src/backend/commands/extension.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2811,6 +2811,59 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
28112811
PG_RETURN_VOID();
28122812
}
28132813

2814+
/*
2815+
* pg_get_loaded_modules
2816+
*
2817+
* SQL-callable function to get per-loaded-module information. Modules
2818+
* (shared libraries) aren't necessarily one-to-one with extensions, but
2819+
* they're sufficiently closely related to make this file a good home.
2820+
*/
2821+
Datum
2822+
pg_get_loaded_modules(PG_FUNCTION_ARGS)
2823+
{
2824+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2825+
DynamicFileList *file_scanner;
2826+
2827+
/* Build tuplestore to hold the result rows */
2828+
InitMaterializedSRF(fcinfo, 0);
2829+
2830+
for (file_scanner = get_first_loaded_module(); file_scanner != NULL;
2831+
file_scanner = get_next_loaded_module(file_scanner))
2832+
{
2833+
const char *library_path,
2834+
*module_name,
2835+
*module_version;
2836+
const char *sep;
2837+
Datum values[3] = {0};
2838+
bool nulls[3] = {0};
2839+
2840+
get_loaded_module_details(file_scanner,
2841+
&library_path,
2842+
&module_name,
2843+
&module_version);
2844+
2845+
if (module_name == NULL)
2846+
nulls[0] = true;
2847+
else
2848+
values[0] = CStringGetTextDatum(module_name);
2849+
if (module_version == NULL)
2850+
nulls[1] = true;
2851+
else
2852+
values[1] = CStringGetTextDatum(module_version);
2853+
2854+
/* For security reasons, we don't show the directory path */
2855+
sep = last_dir_separator(library_path);
2856+
if (sep)
2857+
library_path = sep + 1;
2858+
values[2] = CStringGetTextDatum(library_path);
2859+
2860+
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
2861+
values, nulls);
2862+
}
2863+
2864+
return (Datum) 0;
2865+
}
2866+
28142867
/*
28152868
* extension_config_remove
28162869
*

src/backend/utils/fmgr/dfmgr.c

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,21 @@ typedef struct
4040

4141
/*
4242
* List of dynamically loaded files (kept in malloc'd memory).
43+
*
44+
* Note: "typedef struct DynamicFileList DynamicFileList" appears in fmgr.h.
4345
*/
44-
45-
typedef struct df_files
46+
struct DynamicFileList
4647
{
47-
struct df_files *next; /* List link */
48+
DynamicFileList *next; /* List link */
4849
dev_t device; /* Device file is on */
4950
#ifndef WIN32 /* ensures we never again depend on this under
5051
* win32 */
5152
ino_t inode; /* Inode number of file */
5253
#endif
5354
void *handle; /* a handle for pg_dl* functions */
55+
const Pg_magic_struct *magic; /* Location of module's magic block */
5456
char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
55-
} DynamicFileList;
57+
};
5658

5759
static DynamicFileList *file_list = NULL;
5860
static DynamicFileList *file_tail = NULL;
@@ -68,12 +70,12 @@ char *Dynamic_library_path;
6870

6971
static void *internal_load_library(const char *libname);
7072
pg_noreturn static void incompatible_module_error(const char *libname,
71-
const Pg_magic_struct *module_magic_data);
73+
const Pg_abi_values *module_magic_data);
7274
static char *expand_dynamic_library_name(const char *name);
7375
static void check_restricted_library_name(const char *name);
7476

75-
/* Magic structure that module needs to match to be accepted */
76-
static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
77+
/* ABI values that module needs to match to be accepted */
78+
static const Pg_abi_values magic_data = PG_MODULE_ABI_DATA;
7779

7880

7981
/*
@@ -243,8 +245,10 @@ internal_load_library(const char *libname)
243245
{
244246
const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
245247

246-
if (magic_data_ptr->len != magic_data.len ||
247-
memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
248+
/* Check ABI compatibility fields */
249+
if (magic_data_ptr->len != sizeof(Pg_magic_struct) ||
250+
memcmp(&magic_data_ptr->abi_fields, &magic_data,
251+
sizeof(Pg_abi_values)) != 0)
248252
{
249253
/* copy data block before unlinking library */
250254
Pg_magic_struct module_magic_data = *magic_data_ptr;
@@ -254,8 +258,11 @@ internal_load_library(const char *libname)
254258
free(file_scanner);
255259

256260
/* issue suitable complaint */
257-
incompatible_module_error(libname, &module_magic_data);
261+
incompatible_module_error(libname, &module_magic_data.abi_fields);
258262
}
263+
264+
/* Remember the magic block's location for future use */
265+
file_scanner->magic = magic_data_ptr;
259266
}
260267
else
261268
{
@@ -292,7 +299,7 @@ internal_load_library(const char *libname)
292299
*/
293300
static void
294301
incompatible_module_error(const char *libname,
295-
const Pg_magic_struct *module_magic_data)
302+
const Pg_abi_values *module_magic_data)
296303
{
297304
StringInfoData details;
298305

@@ -393,6 +400,44 @@ incompatible_module_error(const char *libname,
393400
}
394401

395402

403+
/*
404+
* Iterator functions to allow callers to scan the list of loaded modules.
405+
*
406+
* Note: currently, there is no special provision for dealing with changes
407+
* in the list while a scan is happening. Current callers don't need it.
408+
*/
409+
DynamicFileList *
410+
get_first_loaded_module(void)
411+
{
412+
return file_list;
413+
}
414+
415+
DynamicFileList *
416+
get_next_loaded_module(DynamicFileList *dfptr)
417+
{
418+
return dfptr->next;
419+
}
420+
421+
/*
422+
* Return some details about the specified module.
423+
*
424+
* Note that module_name and module_version could be returned as NULL.
425+
*
426+
* We could dispense with this function by exposing struct DynamicFileList
427+
* globally, but this way seems preferable.
428+
*/
429+
void
430+
get_loaded_module_details(DynamicFileList *dfptr,
431+
const char **library_path,
432+
const char **module_name,
433+
const char **module_version)
434+
{
435+
*library_path = dfptr->filename;
436+
*module_name = dfptr->magic->name;
437+
*module_version = dfptr->magic->version;
438+
}
439+
440+
396441
/*
397442
* If name contains a slash, check if the file exists, if so return
398443
* the name. Else (no slash) try to expand using search path (see

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202503261
60+
#define CATALOG_VERSION_NO 202503262
6161

6262
#endif

src/include/catalog/pg_proc.dat

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6756,6 +6756,13 @@
67566756
proargnames => '{rm_id, rm_name, rm_builtin}',
67576757
prosrc => 'pg_get_wal_resource_managers' },
67586758

6759+
{ oid => '8303', descr => 'get info about loaded modules',
6760+
proname => 'pg_get_loaded_modules', prorows => '10', proretset => 't',
6761+
provolatile => 'v', proparallel => 'r', prorettype => 'record',
6762+
proargtypes => '', proallargtypes => '{text,text,text}',
6763+
proargmodes => '{o,o,o}', proargnames => '{module_name,version,file_name}',
6764+
prosrc => 'pg_get_loaded_modules' },
6765+
67596766
{ oid => '2621', descr => 'reload configuration files',
67606767
proname => 'pg_reload_conf', provolatile => 'v', prorettype => 'bool',
67616768
proargtypes => '', prosrc => 'pg_reload_conf' },

0 commit comments

Comments
 (0)