mirror of
https://github.com/postgres/postgres.git
synced 2025-04-18 13:44:19 +03:00
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
This commit is contained in:
parent
e92c0632c1
commit
9324c8c580
@ -22,7 +22,10 @@
|
|||||||
#include "executor/instrument.h"
|
#include "executor/instrument.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
|
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC_EXT(
|
||||||
|
.name = "auto_explain",
|
||||||
|
.version = PG_VERSION
|
||||||
|
);
|
||||||
|
|
||||||
/* GUC variables */
|
/* GUC variables */
|
||||||
static int auto_explain_log_min_duration = -1; /* msec or -1 */
|
static int auto_explain_log_min_duration = -1; /* msec or -1 */
|
||||||
|
@ -212,4 +212,17 @@ REVOKE SET ON PARAMETER auto_explain.log_format FROM regress_user1;
|
|||||||
DROP USER regress_user1;
|
DROP USER regress_user1;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# Test pg_get_loaded_modules() function. This function is particularly
|
||||||
|
# useful for modules with no SQL presence, such as auto_explain.
|
||||||
|
|
||||||
|
my $res = $node->safe_psql(
|
||||||
|
"postgres", q{
|
||||||
|
SELECT module_name,
|
||||||
|
version = current_setting('server_version') as version_ok,
|
||||||
|
regexp_replace(file_name, '\..*', '') as file_name_stripped
|
||||||
|
FROM pg_get_loaded_modules()
|
||||||
|
WHERE module_name = 'auto_explain';
|
||||||
|
});
|
||||||
|
like($res, qr/^auto_explain\|t\|auto_explain$/, "pg_get_loaded_modules() ok");
|
||||||
|
|
||||||
done_testing();
|
done_testing();
|
||||||
|
@ -25040,6 +25040,28 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
|
|||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="func_table_entry"><para role="func_signature">
|
||||||
|
<indexterm>
|
||||||
|
<primary>pg_get_loaded_modules</primary>
|
||||||
|
</indexterm>
|
||||||
|
<function>pg_get_loaded_modules</function> ()
|
||||||
|
<returnvalue>setof record</returnvalue>
|
||||||
|
( <parameter>module_name</parameter> <type>text</type>,
|
||||||
|
<parameter>version</parameter> <type>text</type>,
|
||||||
|
<parameter>file_name</parameter> <type>text</type> )
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Returns a list of the loadable modules that are loaded into the
|
||||||
|
current server session. The <parameter>module_name</parameter>
|
||||||
|
and <parameter>version</parameter> fields are NULL unless the
|
||||||
|
module author supplied values for them using
|
||||||
|
the <literal>PG_MODULE_MAGIC_EXT</literal> macro.
|
||||||
|
The <parameter>file_name</parameter> field gives the file
|
||||||
|
name of the module (shared library).
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry role="func_table_entry"><para role="func_signature">
|
<entry role="func_table_entry"><para role="func_signature">
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
@ -1954,6 +1954,9 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision
|
|||||||
<indexterm zone="xfunc-c-dynload">
|
<indexterm zone="xfunc-c-dynload">
|
||||||
<primary>magic block</primary>
|
<primary>magic block</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
<indexterm zone="xfunc-c-dynload">
|
||||||
|
<primary><literal>PG_MODULE_MAGIC</literal></primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To ensure that a dynamically loaded object file is not loaded into an
|
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
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
PG_MODULE_MAGIC;
|
PG_MODULE_MAGIC;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
or
|
||||||
|
<programlisting>
|
||||||
|
PG_MODULE_MAGIC_EXT(<replaceable>parameters</replaceable>);
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The <literal>PG_MODULE_MAGIC_EXT</literal> variant allows the specification
|
||||||
|
of additional information about the module; currently, a name and/or a
|
||||||
|
version string can be added. (More fields might be allowed in future.)
|
||||||
|
Write something like this:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
PG_MODULE_MAGIC_EXT(
|
||||||
|
.name = "my_module_name",
|
||||||
|
.version = "1.2.3"
|
||||||
|
);
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
Subsequently the name and version can be examined via
|
||||||
|
the <function>pg_get_loaded_modules()</function> function.
|
||||||
|
The meaning of the version string is not restricted
|
||||||
|
by <productname>PostgreSQL</productname>, but use of semantic versioning
|
||||||
|
rules is recommended.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -2811,6 +2811,59 @@ pg_extension_config_dump(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pg_get_loaded_modules
|
||||||
|
*
|
||||||
|
* SQL-callable function to get per-loaded-module information. Modules
|
||||||
|
* (shared libraries) aren't necessarily one-to-one with extensions, but
|
||||||
|
* they're sufficiently closely related to make this file a good home.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_get_loaded_modules(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||||
|
DynamicFileList *file_scanner;
|
||||||
|
|
||||||
|
/* Build tuplestore to hold the result rows */
|
||||||
|
InitMaterializedSRF(fcinfo, 0);
|
||||||
|
|
||||||
|
for (file_scanner = get_first_loaded_module(); file_scanner != NULL;
|
||||||
|
file_scanner = get_next_loaded_module(file_scanner))
|
||||||
|
{
|
||||||
|
const char *library_path,
|
||||||
|
*module_name,
|
||||||
|
*module_version;
|
||||||
|
const char *sep;
|
||||||
|
Datum values[3] = {0};
|
||||||
|
bool nulls[3] = {0};
|
||||||
|
|
||||||
|
get_loaded_module_details(file_scanner,
|
||||||
|
&library_path,
|
||||||
|
&module_name,
|
||||||
|
&module_version);
|
||||||
|
|
||||||
|
if (module_name == NULL)
|
||||||
|
nulls[0] = true;
|
||||||
|
else
|
||||||
|
values[0] = CStringGetTextDatum(module_name);
|
||||||
|
if (module_version == NULL)
|
||||||
|
nulls[1] = true;
|
||||||
|
else
|
||||||
|
values[1] = CStringGetTextDatum(module_version);
|
||||||
|
|
||||||
|
/* For security reasons, we don't show the directory path */
|
||||||
|
sep = last_dir_separator(library_path);
|
||||||
|
if (sep)
|
||||||
|
library_path = sep + 1;
|
||||||
|
values[2] = CStringGetTextDatum(library_path);
|
||||||
|
|
||||||
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
|
||||||
|
values, nulls);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Datum) 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* extension_config_remove
|
* extension_config_remove
|
||||||
*
|
*
|
||||||
|
@ -40,19 +40,21 @@ typedef struct
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* List of dynamically loaded files (kept in malloc'd memory).
|
* List of dynamically loaded files (kept in malloc'd memory).
|
||||||
|
*
|
||||||
|
* Note: "typedef struct DynamicFileList DynamicFileList" appears in fmgr.h.
|
||||||
*/
|
*/
|
||||||
|
struct DynamicFileList
|
||||||
typedef struct df_files
|
|
||||||
{
|
{
|
||||||
struct df_files *next; /* List link */
|
DynamicFileList *next; /* List link */
|
||||||
dev_t device; /* Device file is on */
|
dev_t device; /* Device file is on */
|
||||||
#ifndef WIN32 /* ensures we never again depend on this under
|
#ifndef WIN32 /* ensures we never again depend on this under
|
||||||
* win32 */
|
* win32 */
|
||||||
ino_t inode; /* Inode number of file */
|
ino_t inode; /* Inode number of file */
|
||||||
#endif
|
#endif
|
||||||
void *handle; /* a handle for pg_dl* functions */
|
void *handle; /* a handle for pg_dl* functions */
|
||||||
|
const Pg_magic_struct *magic; /* Location of module's magic block */
|
||||||
char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
|
char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
|
||||||
} DynamicFileList;
|
};
|
||||||
|
|
||||||
static DynamicFileList *file_list = NULL;
|
static DynamicFileList *file_list = NULL;
|
||||||
static DynamicFileList *file_tail = NULL;
|
static DynamicFileList *file_tail = NULL;
|
||||||
@ -68,12 +70,12 @@ char *Dynamic_library_path;
|
|||||||
|
|
||||||
static void *internal_load_library(const char *libname);
|
static void *internal_load_library(const char *libname);
|
||||||
pg_noreturn static void incompatible_module_error(const char *libname,
|
pg_noreturn static void incompatible_module_error(const char *libname,
|
||||||
const Pg_magic_struct *module_magic_data);
|
const Pg_abi_values *module_magic_data);
|
||||||
static char *expand_dynamic_library_name(const char *name);
|
static char *expand_dynamic_library_name(const char *name);
|
||||||
static void check_restricted_library_name(const char *name);
|
static void check_restricted_library_name(const char *name);
|
||||||
|
|
||||||
/* Magic structure that module needs to match to be accepted */
|
/* ABI values that module needs to match to be accepted */
|
||||||
static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
|
static const Pg_abi_values magic_data = PG_MODULE_ABI_DATA;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -243,8 +245,10 @@ internal_load_library(const char *libname)
|
|||||||
{
|
{
|
||||||
const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
|
const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
|
||||||
|
|
||||||
if (magic_data_ptr->len != magic_data.len ||
|
/* Check ABI compatibility fields */
|
||||||
memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
|
if (magic_data_ptr->len != sizeof(Pg_magic_struct) ||
|
||||||
|
memcmp(&magic_data_ptr->abi_fields, &magic_data,
|
||||||
|
sizeof(Pg_abi_values)) != 0)
|
||||||
{
|
{
|
||||||
/* copy data block before unlinking library */
|
/* copy data block before unlinking library */
|
||||||
Pg_magic_struct module_magic_data = *magic_data_ptr;
|
Pg_magic_struct module_magic_data = *magic_data_ptr;
|
||||||
@ -254,8 +258,11 @@ internal_load_library(const char *libname)
|
|||||||
free(file_scanner);
|
free(file_scanner);
|
||||||
|
|
||||||
/* issue suitable complaint */
|
/* issue suitable complaint */
|
||||||
incompatible_module_error(libname, &module_magic_data);
|
incompatible_module_error(libname, &module_magic_data.abi_fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remember the magic block's location for future use */
|
||||||
|
file_scanner->magic = magic_data_ptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -292,7 +299,7 @@ internal_load_library(const char *libname)
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
incompatible_module_error(const char *libname,
|
incompatible_module_error(const char *libname,
|
||||||
const Pg_magic_struct *module_magic_data)
|
const Pg_abi_values *module_magic_data)
|
||||||
{
|
{
|
||||||
StringInfoData details;
|
StringInfoData details;
|
||||||
|
|
||||||
@ -393,6 +400,44 @@ incompatible_module_error(const char *libname,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterator functions to allow callers to scan the list of loaded modules.
|
||||||
|
*
|
||||||
|
* Note: currently, there is no special provision for dealing with changes
|
||||||
|
* in the list while a scan is happening. Current callers don't need it.
|
||||||
|
*/
|
||||||
|
DynamicFileList *
|
||||||
|
get_first_loaded_module(void)
|
||||||
|
{
|
||||||
|
return file_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicFileList *
|
||||||
|
get_next_loaded_module(DynamicFileList *dfptr)
|
||||||
|
{
|
||||||
|
return dfptr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return some details about the specified module.
|
||||||
|
*
|
||||||
|
* Note that module_name and module_version could be returned as NULL.
|
||||||
|
*
|
||||||
|
* We could dispense with this function by exposing struct DynamicFileList
|
||||||
|
* globally, but this way seems preferable.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
get_loaded_module_details(DynamicFileList *dfptr,
|
||||||
|
const char **library_path,
|
||||||
|
const char **module_name,
|
||||||
|
const char **module_version)
|
||||||
|
{
|
||||||
|
*library_path = dfptr->filename;
|
||||||
|
*module_name = dfptr->magic->name;
|
||||||
|
*module_version = dfptr->magic->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If name contains a slash, check if the file exists, if so return
|
* If name contains a slash, check if the file exists, if so return
|
||||||
* the name. Else (no slash) try to expand using search path (see
|
* the name. Else (no slash) try to expand using search path (see
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202503261
|
#define CATALOG_VERSION_NO 202503262
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6756,6 +6756,13 @@
|
|||||||
proargnames => '{rm_id, rm_name, rm_builtin}',
|
proargnames => '{rm_id, rm_name, rm_builtin}',
|
||||||
prosrc => 'pg_get_wal_resource_managers' },
|
prosrc => 'pg_get_wal_resource_managers' },
|
||||||
|
|
||||||
|
{ oid => '8303', descr => 'get info about loaded modules',
|
||||||
|
proname => 'pg_get_loaded_modules', prorows => '10', proretset => 't',
|
||||||
|
provolatile => 'v', proparallel => 'r', prorettype => 'record',
|
||||||
|
proargtypes => '', proallargtypes => '{text,text,text}',
|
||||||
|
proargmodes => '{o,o,o}', proargnames => '{module_name,version,file_name}',
|
||||||
|
prosrc => 'pg_get_loaded_modules' },
|
||||||
|
|
||||||
{ oid => '2621', descr => 'reload configuration files',
|
{ oid => '2621', descr => 'reload configuration files',
|
||||||
proname => 'pg_reload_conf', provolatile => 'v', prorettype => 'bool',
|
proname => 'pg_reload_conf', provolatile => 'v', prorettype => 'bool',
|
||||||
proargtypes => '', prosrc => 'pg_reload_conf' },
|
proargtypes => '', prosrc => 'pg_reload_conf' },
|
||||||
|
@ -440,41 +440,52 @@ extern PGDLLEXPORT void _PG_init(void);
|
|||||||
* We require dynamically-loaded modules to include the macro call
|
* We require dynamically-loaded modules to include the macro call
|
||||||
* PG_MODULE_MAGIC;
|
* PG_MODULE_MAGIC;
|
||||||
* so that we can check for obvious incompatibility, such as being compiled
|
* so that we can check for obvious incompatibility, such as being compiled
|
||||||
* for a different major PostgreSQL version.
|
* for a different major PostgreSQL version. Alternatively, write
|
||||||
|
* PG_MODULE_MAGIC_EXT(...);
|
||||||
|
* where the optional arguments can specify module name and version, and
|
||||||
|
* perhaps other values in future. Note that in a multiple-source-file
|
||||||
|
* module, there should be exactly one such macro call.
|
||||||
*
|
*
|
||||||
* To compile with versions of PostgreSQL that do not support this,
|
* You may need an #ifdef test to verify that the version of PostgreSQL
|
||||||
* you may put an #ifdef/#endif test around it. Note that in a multiple-
|
* you are compiling against supports PG_MODULE_MAGIC_EXT().
|
||||||
* source-file module, the macro call should only appear once.
|
|
||||||
*
|
*
|
||||||
* The specific items included in the magic block are intended to be ones that
|
* The specific items included in the ABI fields are intended to be ones that
|
||||||
* are custom-configurable and especially likely to break dynamically loaded
|
* are custom-configurable and especially likely to break dynamically loaded
|
||||||
* modules if they were compiled with other values. Also, the length field
|
* modules if they were compiled with other values. Also, the length field
|
||||||
* can be used to detect definition changes.
|
* can be used to detect definition changes.
|
||||||
*
|
*
|
||||||
* Note: we compare magic blocks with memcmp(), so there had better not be
|
* Note: we compare Pg_abi_values structs with memcmp(), so there had better
|
||||||
* any alignment pad bytes in them.
|
* not be any alignment pad bytes in them.
|
||||||
*
|
*
|
||||||
* Note: when changing the contents of magic blocks, be sure to adjust the
|
* Note: when changing the contents of Pg_abi_values, be sure to adjust the
|
||||||
* incompatible_module_error() function in dfmgr.c.
|
* incompatible_module_error() function in dfmgr.c.
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Definition of the magic block structure */
|
/* Definition of the values we check to verify ABI compatibility */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int len; /* sizeof(this struct) */
|
|
||||||
int version; /* PostgreSQL major version */
|
int version; /* PostgreSQL major version */
|
||||||
int funcmaxargs; /* FUNC_MAX_ARGS */
|
int funcmaxargs; /* FUNC_MAX_ARGS */
|
||||||
int indexmaxkeys; /* INDEX_MAX_KEYS */
|
int indexmaxkeys; /* INDEX_MAX_KEYS */
|
||||||
int namedatalen; /* NAMEDATALEN */
|
int namedatalen; /* NAMEDATALEN */
|
||||||
int float8byval; /* FLOAT8PASSBYVAL */
|
int float8byval; /* FLOAT8PASSBYVAL */
|
||||||
char abi_extra[32]; /* see pg_config_manual.h */
|
char abi_extra[32]; /* see pg_config_manual.h */
|
||||||
|
} Pg_abi_values;
|
||||||
|
|
||||||
|
/* Definition of the magic block structure */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int len; /* sizeof(this struct) */
|
||||||
|
Pg_abi_values abi_fields; /* see above */
|
||||||
|
/* Remaining fields are zero unless filled via PG_MODULE_MAGIC_EXT */
|
||||||
|
const char *name; /* optional module name */
|
||||||
|
const char *version; /* optional module version */
|
||||||
} Pg_magic_struct;
|
} Pg_magic_struct;
|
||||||
|
|
||||||
/* The actual data block contents */
|
/* Macro to fill the ABI fields */
|
||||||
#define PG_MODULE_MAGIC_DATA \
|
#define PG_MODULE_ABI_DATA \
|
||||||
{ \
|
{ \
|
||||||
sizeof(Pg_magic_struct), \
|
|
||||||
PG_VERSION_NUM / 100, \
|
PG_VERSION_NUM / 100, \
|
||||||
FUNC_MAX_ARGS, \
|
FUNC_MAX_ARGS, \
|
||||||
INDEX_MAX_KEYS, \
|
INDEX_MAX_KEYS, \
|
||||||
@ -483,7 +494,18 @@ typedef struct
|
|||||||
FMGR_ABI_EXTRA, \
|
FMGR_ABI_EXTRA, \
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_magic_struct *) 0)->abi_extra),
|
/*
|
||||||
|
* Macro to fill a magic block. If any arguments are given, they should
|
||||||
|
* be field initializers.
|
||||||
|
*/
|
||||||
|
#define PG_MODULE_MAGIC_DATA(...) \
|
||||||
|
{ \
|
||||||
|
.len = sizeof(Pg_magic_struct), \
|
||||||
|
.abi_fields = PG_MODULE_ABI_DATA, \
|
||||||
|
__VA_ARGS__ \
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticAssertDecl(sizeof(FMGR_ABI_EXTRA) <= sizeof(((Pg_abi_values *) 0)->abi_extra),
|
||||||
"FMGR_ABI_EXTRA too long");
|
"FMGR_ABI_EXTRA too long");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -500,7 +522,26 @@ extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
|
|||||||
const Pg_magic_struct * \
|
const Pg_magic_struct * \
|
||||||
PG_MAGIC_FUNCTION_NAME(void) \
|
PG_MAGIC_FUNCTION_NAME(void) \
|
||||||
{ \
|
{ \
|
||||||
static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \
|
static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA(0); \
|
||||||
|
return &Pg_magic_data; \
|
||||||
|
} \
|
||||||
|
extern int no_such_variable
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Alternate declaration that allows specification of additional fields.
|
||||||
|
* The additional values should be written as field initializers, for example
|
||||||
|
* PG_MODULE_MAGIC_EXT(
|
||||||
|
* .name = "some string",
|
||||||
|
* .version = "some string"
|
||||||
|
* );
|
||||||
|
*/
|
||||||
|
#define PG_MODULE_MAGIC_EXT(...) \
|
||||||
|
extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
|
||||||
|
const Pg_magic_struct * \
|
||||||
|
PG_MAGIC_FUNCTION_NAME(void) \
|
||||||
|
{ \
|
||||||
|
static const Pg_magic_struct Pg_magic_data = \
|
||||||
|
PG_MODULE_MAGIC_DATA(__VA_ARGS__); \
|
||||||
return &Pg_magic_data; \
|
return &Pg_magic_data; \
|
||||||
} \
|
} \
|
||||||
extern int no_such_variable
|
extern int no_such_variable
|
||||||
@ -738,6 +779,8 @@ extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid);
|
|||||||
/*
|
/*
|
||||||
* Routines in dfmgr.c
|
* Routines in dfmgr.c
|
||||||
*/
|
*/
|
||||||
|
typedef struct DynamicFileList DynamicFileList; /* opaque outside dfmgr.c */
|
||||||
|
|
||||||
extern PGDLLIMPORT char *Dynamic_library_path;
|
extern PGDLLIMPORT char *Dynamic_library_path;
|
||||||
|
|
||||||
extern char *substitute_path_macro(const char *str, const char *macro, const char *value);
|
extern char *substitute_path_macro(const char *str, const char *macro, const char *value);
|
||||||
@ -747,6 +790,12 @@ extern void *load_external_function(const char *filename, const char *funcname,
|
|||||||
bool signalNotFound, void **filehandle);
|
bool signalNotFound, void **filehandle);
|
||||||
extern void *lookup_external_function(void *filehandle, const char *funcname);
|
extern void *lookup_external_function(void *filehandle, const char *funcname);
|
||||||
extern void load_file(const char *filename, bool restricted);
|
extern void load_file(const char *filename, bool restricted);
|
||||||
|
extern DynamicFileList *get_first_loaded_module(void);
|
||||||
|
extern DynamicFileList *get_next_loaded_module(DynamicFileList *dfptr);
|
||||||
|
extern void get_loaded_module_details(DynamicFileList *dfptr,
|
||||||
|
const char **library_path,
|
||||||
|
const char **module_name,
|
||||||
|
const char **module_version);
|
||||||
extern void **find_rendezvous_variable(const char *varName);
|
extern void **find_rendezvous_variable(const char *varName);
|
||||||
extern Size EstimateLibraryStateSpace(void);
|
extern Size EstimateLibraryStateSpace(void);
|
||||||
extern void SerializeLibraryState(Size maxsize, char *start_address);
|
extern void SerializeLibraryState(Size maxsize, char *start_address);
|
||||||
|
@ -2229,6 +2229,7 @@ PgStat_WalCounters
|
|||||||
PgStat_WalStats
|
PgStat_WalStats
|
||||||
PgXmlErrorContext
|
PgXmlErrorContext
|
||||||
PgXmlStrictness
|
PgXmlStrictness
|
||||||
|
Pg_abi_values
|
||||||
Pg_finfo_record
|
Pg_finfo_record
|
||||||
Pg_magic_struct
|
Pg_magic_struct
|
||||||
PipeProtoChunk
|
PipeProtoChunk
|
||||||
|
Loading…
x
Reference in New Issue
Block a user