System views for versions reporting
Hi,
Based on the feedback in [1]/messages/by-id/znc72ymyoelvk5rjk5ub254v3qvcczfrk6autygjdobfvx2e7p@s3dssvf34twa, here is my attempt at implementing system
views for versions reporting. It adds pg_system_versions for showing
things like core version, compiler, LLVM, etc, and pg_system_libraries
for showing linked shared objects. I think everyone has ageed that the
first was a good idea, where the second was only suggested -- after some
thinking I find shared obects useful enough to include here as well.
The main idea is to facilitate bug reporting. In particular, in many JIT
related bug reports one of the first questions is often "which LLVM
version is used?". Gathering such information is a manual process,
mistakes could be made when veryfing llvm-config output or anywhere
else. Having a system view for such purposes makes the process a bit
more robust.
The first three patches are essential for this purpose, the fourth one
is somewhat independent and could be concidered in isolation. The output
looks like this :
=# select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Compile Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
LLVM | 18.1.6 | Run Time
=# select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]
Any feedback is appreciated.
0001-Add-infrastructure-for-pg_system_versions-view
Prepares the infrastructure, adds the view without populating it with
the data just yet. The view is backed by a hash table, which contains
callbacks returning a version string for a particular component. The
idea is to allow some flexibility in reporting, making components
responsible for how and when the information is exposed. E.g. it allows
to say that the version is not available.
0002-Add-core-versions-to-pg_system_versions.patch
Actually populates the view with some predefined compile time versions.
The versions are registered in PostgresMain, right after
BeginReportingGUCOptions -- there is no strong reasoning for that except
that it looks fine to me, so feel free to suggest a better place.
0003-Add-JIT-provider-version-to-pg_system_versions.patch
Finally adds LLVM version into the view via extending set of JIT
provider callbacks. The registration is happening together with the core
versions from the previous patch, because the JIT provider itself is
initialized only when a first expression is getting compiled.
0004-Add-pg_system_libraries-view.patch
Strictly speaking independent from the above patches. Adds the view to
show linked shared objects by iterating dl_phdr_info with
dl_iterate_phdr. It belongs to the standard C library, so I assume it's
portable enough.
[1]: /messages/by-id/znc72ymyoelvk5rjk5ub254v3qvcczfrk6autygjdobfvx2e7p@s3dssvf34twa
Attachments:
v1-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From d903142d178dd8a9c03d07ee4809ac582b9b7818 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:27:57 +0200
Subject: [PATCH v1 1/4] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 56 ++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 108 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 40 +++++++++
src/test/regress/expected/rules.out | 8 ++
8 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 634a4c0..df0b9d3 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -226,6 +226,11 @@
<entry>wait events</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5064,4 +5069,55 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-version">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about versions of various system components, e.g. PostgreSQL itself,
+ compiler used to build it, dependencies, etc.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7fd5d25..091013d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1377,3 +1377,11 @@ CREATE VIEW pg_stat_subscription_stats AS
CREATE VIEW pg_wait_events AS
SELECT * FROM pg_get_wait_events();
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index d9f5978..fcb3be7 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -31,7 +31,8 @@ OBJS = \
sampling.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 6669502..ca2abc5 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -15,6 +15,7 @@ backend_sources += files(
'rls.c',
'sampling.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 0000000..4d633fc
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,108 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unicode/uchar.h>
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char* name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+ ctl.hcxt = CurrentMemoryContext;
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicated system version")));
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char* version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = hentry->type;
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 43f608d..59587db 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12316,4 +12316,10 @@
proargtypes => 'int2',
prosrc => 'gist_stratnum_identity' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 0000000..a73f046
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#include <link.h>
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char* (*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a1626f3..b9ad6f5 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2611,6 +2611,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
base-commit: 6aa44060a3c94ee10273bb8a89e98a5bb2fbbacb
--
2.45.1
v1-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 13d7dfb34dc9286379e55157c847b01cf77b5097 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:30:34 +0200
Subject: [PATCH v1 2/4] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, PostgreSQL itself and compiler which was used
to build everything. All of them are compile time versions. Register the
core versions at the backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Compile Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
---
configure | 12 +++++++
configure.ac | 4 +++
meson.build | 4 +++
src/backend/tcop/postgres.c | 12 +++++++
src/backend/utils/misc/system_version.c | 46 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 +++
src/include/utils/system_version.h | 7 ++++
src/test/regress/expected/sysviews.out | 8 +++++
src/test/regress/sql/sysviews.sql | 4 +++
9 files changed, 101 insertions(+)
diff --git a/configure b/configure
index 53c8a1f..63e6d3e 100755
--- a/configure
+++ b/configure
@@ -19223,6 +19223,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index 6a35b28..d063504 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2417,6 +2417,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index 7150f85..c5dfba6 100644
--- a/meson.build
+++ b/meson.build
@@ -2756,6 +2756,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7f5eada..3b45fdc 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -80,6 +80,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -197,6 +198,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4369,6 +4371,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5282,3 +5287,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 4d633fc..8ebd0c3 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -67,6 +67,52 @@ add_system_version(const char* name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+ add_system_version("ICU", icu_get_version, CompileTime);
+}
+
+const char*
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_VERSION);
+}
+
+const char*
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_ARCH_STR);
+}
+
+const char*
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_CC_STR);
+}
+
+const char*
+icu_get_version(bool *available)
+{
+#ifdef USE_ICU
+ *available = true;
+ return (const char*) U_UNICODE_VERSION;
+#else
+ *available = false;
+ return (const char*) "";
+#endif
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3800636..b0a588b 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -621,6 +621,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index a73f046..9537f47 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -36,5 +36,12 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
+
+const char* core_get_version(bool *available);
+const char* core_get_arch(bool *available);
+const char* core_get_compiler(bool *available);
+
+const char* icu_get_version(bool *available);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index fad7fc3..0afabc1 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -222,3 +222,11 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
t
(1 row)
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index b2a7923..7a5a5f6 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -98,3 +98,7 @@ set timezone_abbreviations = 'Australia';
select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
set timezone_abbreviations = 'India';
select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
+
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
--
2.45.1
v1-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From b50af6045561104f9021b1da7f3e18aebbb659f1 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v1 3/4] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 19 +++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 17 +++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/include/jit/jit.h | 11 +++++++++++
src/include/jit/llvmjit.h | 2 ++
5 files changed, 56 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index 815b58f..8085a43 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,22 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Return JIT provider's version string for troubleshooting purposes.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ if (provider_init())
+ return provider.get_version(available);
+
+ *available = false;
+ return "";
+}
+
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 0f6cec5..2eed882 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -165,6 +165,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1382,3 +1383,19 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version(bool *available)
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major, minor, patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ *available = true;
+ return (const char*) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ *available = false;
+ return "";
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3b45fdc..84a2a44 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5293,4 +5293,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index d9a080c..6b3d453 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -13,6 +13,7 @@
#include "executor/instrument.h"
#include "utils/resowner.h"
+#include "utils/system_version.h"
/* Flags determining what kind of JIT operations to perform */
@@ -70,12 +71,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char* (*JitProviderVersion) (bool *available);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +105,13 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Get the provider's version string. The flag indicating availability is
+ * passed as an argument, and will be set accordingly if it's not possible to
+ * get the version.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 420775b..898848a 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -136,6 +136,8 @@ extern LLVMValueRef slot_compile_deform(struct LLVMJitContext *context, TupleDes
extern LLVMTypeRef LLVMGetFunctionReturnType(LLVMValueRef r);
extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
+extern const char* llvm_version(bool *available);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.45.1
v1-0004-Add-pg_system_libraries-view.patchtext/plain; charset=us-asciiDownload
From caa461373cd14a7cfa41b6a37f0d2c7577c0c935 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:32:04 +0200
Subject: [PATCH v1 4/4] Add pg_system_libraries view
Introduce a way to report shared objects linked with PostgreSQL. Such
information is useful for troubleshooting, and could enhance bug reports. The
reporting is done via pg_system_libraries view, which contains a file path to
the shared object. It's implemented via standard C library dl_iterate_phdr,
which should be portable enough.
select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]
---
doc/src/sgml/system-views.sgml | 46 ++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/tcop/postgres.c | 1 +
src/backend/utils/misc/system_version.c | 81 +++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 9 +++
src/test/regress/expected/rules.out | 2 +
src/test/regress/expected/sysviews.out | 7 +++
src/test/regress/sql/sysviews.sql | 2 +
9 files changed, 157 insertions(+)
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index df0b9d3..bf397f5 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -231,6 +231,11 @@
<entry>system versions</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-libraries"><structname>pg_system_libraries</structname></link></entry>
+ <entry>linked libraries</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5120,4 +5125,45 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-libraries">
+ <title><structname>pg_system_libraries</structname></title>
+
+ <indexterm zone="view-pg-system-libraries">
+ <primary>pg_system_libraries</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_libraries</structname> provides description
+ about shared objects PostgreSQL is linked with.
+ </para>
+
+ <table>
+ <title><structname>pg_system_libraries</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Shared object file path
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 091013d..26f087c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1385,3 +1385,6 @@ CREATE VIEW pg_system_versions AS
WHEN 1 THEN 'Run Time'
END AS "type"
FROM pg_get_system_versions();
+
+CREATE VIEW pg_system_libraries AS
+ SELECT * FROM pg_get_system_libraries() as f(name text);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 84a2a44..80c5372 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4373,6 +4373,7 @@ PostgresMain(const char *dbname, const char *username)
/* Prepare information for reporting versions and libraries. */
register_system_versions();
+ register_libraries();
/*
* Also set up handler to log session end; we have to wait till now to be
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 8ebd0c3..73e8078 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -14,6 +14,10 @@
* a version string directly allows for more flexibility about how and when the
* information could be reported.
*
+ * Libraries reporting is implemented similarly via a hash table containing
+ * only the library file path. This information is populated directly during
+ * the initialization.
+ *
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -32,6 +36,7 @@
#include "utils/system_version.h"
static HTAB *versions = NULL;
+static HTAB *libraries = NULL;
void
add_system_version(const char* name, SystemVersionCB cb, VersionType type)
@@ -152,3 +157,79 @@ pg_get_system_versions(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+
+/*
+ * Walk through list of shared objects and populate the libraries hash table.
+ */
+void
+register_libraries()
+{
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemLibrary);
+ ctl.hcxt = CurrentMemoryContext;
+
+ libraries = hash_create("Libraries table",
+ MAX_SYSTEM_LIBRARIES,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+
+ dl_iterate_phdr(add_library, NULL);
+}
+
+int add_library(struct dl_phdr_info *info, size_t size, void *data)
+{
+ const char *key;
+ bool found;
+
+ if (strcmp(info->dlpi_name, "") == 0)
+ {
+ /* The first visited object is the main program with the empty name,
+ * which is not so interesting. */
+ return 0;
+ }
+
+ key = pstrdup(info->dlpi_name);
+ hash_search(libraries, key, HASH_ENTER, &found);
+
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicated library")));
+
+ return 0;
+}
+
+/*
+ * pg_get_libraries
+ *
+ * List information about shared objects.
+ */
+Datum
+pg_get_system_libraries(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_LIBRARIES_COLS 1
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemLibrary *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, libraries);
+ while ((hentry = (SystemLibrary *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_LIBRARIES_COLS] = {0};
+ bool nulls[PG_GET_SYS_LIBRARIES_COLS] = {0};
+
+ values[0] = CStringGetTextDatum(hentry->filepath);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 59587db..f00a562 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12322,4 +12322,10 @@
proargtypes => '', proallargtypes => '{text,text,int8}',
proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
prosrc => 'pg_get_system_versions' },
+{ oid => '9433', descr => 'describe system libraries',
+ proname => 'pg_get_system_libraries', procost => '10', prorows => '30',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text}',
+ proargmodes => '{o}', proargnames => '{name}',
+ prosrc => 'pg_get_system_libraries' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 9537f47..01551fd 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -14,6 +14,7 @@
#include <link.h>
#define MAX_SYSTEM_VERSIONS 100
+#define MAX_SYSTEM_LIBRARIES 100
typedef enum VersionType
{
@@ -35,8 +36,14 @@ typedef struct SystemVersion
SystemVersionCB callback; /* Callback to fetch the version string */
} SystemVersion;
+typedef struct SystemLibrary
+{
+ char filepath[NAMEDATALEN];
+} SystemLibrary;
+
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
extern void register_core_versions(void);
+extern void register_libraries(void);
const char* core_get_version(bool *available);
const char* core_get_arch(bool *available);
@@ -44,4 +51,6 @@ const char* core_get_compiler(bool *available);
const char* icu_get_version(bool *available);
+int add_library(struct dl_phdr_info *info, size_t size, void *data);
+
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index b9ad6f5..3ba2510 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2611,6 +2611,8 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_libraries| SELECT name
+ FROM pg_get_system_libraries() f(name text);
pg_system_versions| SELECT name,
version,
CASE type
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 0afabc1..c8b14c1 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -230,3 +230,10 @@ select count(*) >= 4 as ok FROM pg_system_versions;
t
(1 row)
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 7a5a5f6..1334574 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -102,3 +102,5 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- At least 4 core versions should be present, architecture, ICU, core and
-- compiler
select count(*) >= 4 as ok FROM pg_system_versions;
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
--
2.45.1
On 10/6/24 11:36, Dmitry Dolgov wrote:
Hi,
Based on the feedback in [1], here is my attempt at implementing system
views for versions reporting. It adds pg_system_versions for showing
things like core version, compiler, LLVM, etc, and pg_system_libraries
for showing linked shared objects. I think everyone has ageed that the
first was a good idea, where the second was only suggested -- after some
thinking I find shared obects useful enough to include here as well.The main idea is to facilitate bug reporting. In particular, in many JIT
related bug reports one of the first questions is often "which LLVM
version is used?". Gathering such information is a manual process,
mistakes could be made when veryfing llvm-config output or anywhere
else. Having a system view for such purposes makes the process a bit
more robust.The first three patches are essential for this purpose, the fourth one
is somewhat independent and could be concidered in isolation. The output
looks like this :=# select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Compile Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
LLVM | 18.1.6 | Run Time
I'm not sure why ICU is "Compile Time" rather than "Run Time" when it is
not statically linked. Also, if we are going to include ICU here,
shouldn't we also include libc version?
=# select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]Any feedback is appreciated.
I think it would be nice to include a sha256 hash (or something similar)
of the libraries as well, so that they can be checked against known good
values.
--
Joe Conway
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
Joe Conway <mail@joeconway.com> writes:
I think it would be nice to include a sha256 hash (or something similar)
of the libraries as well, so that they can be checked against known good
values.
That seems well outside the charter of this patch. Also, how would
we even get that information? A typical application doesn't know
exactly what libraries it's linked with or where they came from on
the filesystem. Maybe one could find that out with sufficient
platform-specific hackery, but I don't believe we could do it
portably.
regards, tom lane
On Sun, Oct 06, 2024 at 12:01:29PM GMT, Joe Conway wrote:
On 10/6/24 11:36, Dmitry Dolgov wrote:Hi,
Based on the feedback in [1], here is my attempt at implementing system
views for versions reporting. It adds pg_system_versions for showing
things like core version, compiler, LLVM, etc, and pg_system_libraries
for showing linked shared objects. I think everyone has ageed that the
first was a good idea, where the second was only suggested -- after some
thinking I find shared obects useful enough to include here as well.The main idea is to facilitate bug reporting. In particular, in many JIT
related bug reports one of the first questions is often "which LLVM
version is used?". Gathering such information is a manual process,
mistakes could be made when veryfing llvm-config output or anywhere
else. Having a system view for such purposes makes the process a bit
more robust.The first three patches are essential for this purpose, the fourth one
is somewhat independent and could be concidered in isolation. The output
looks like this :=# select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Compile Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
LLVM | 18.1.6 | Run TimeI'm not sure why ICU is "Compile Time" rather than "Run Time" when it is not
statically linked.
It reports U_UNICODE_VERSION at compile time. It's not necessarily
correct though, I can try to replace it with the runtime version. I
think there was some ICU functionality (something like
u_getUnicodeVersion), which is maybe a better fit.
Also, if we are going to include ICU here, shouldn't we
also include libc version?
Yeah, why not. One of my goals here is to identify a balanced set of
useful versions to report.
=# select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]Any feedback is appreciated.
I think it would be nice to include a sha256 hash (or something similar) of
the libraries as well, so that they can be checked against known good
values.
I was thinking about getting more info to show in this view, but haven't
found any reasonable way to achieve that. So I would agree with Tom on
that.
On 06.10.24 17:36, Dmitry Dolgov wrote:
Based on the feedback in [1], here is my attempt at implementing system
views for versions reporting. It adds pg_system_versions for showing
things like core version, compiler, LLVM, etc, and pg_system_libraries
for showing linked shared objects.
Is a system view the right interface? For example, in pgbouncer we just
added it to the version output:
$ pgbouncer --version
PgBouncer 1.18.0
libevent 2.1.12-stable
adns: c-ares 1.19.0
tls: OpenSSL 3.3.2 3 Sep 2024
That way, you can get this information without having to start a server
instance. (Maybe you can't start a server instance because it just
crashed because of some library version issue ...)
On 10/16/24 08:47, Peter Eisentraut wrote:
On 06.10.24 17:36, Dmitry Dolgov wrote:
Based on the feedback in [1], here is my attempt at implementing system
views for versions reporting. It adds pg_system_versions for showing
things like core version, compiler, LLVM, etc, and pg_system_libraries
for showing linked shared objects.Is a system view the right interface? For example, in pgbouncer we just
added it to the version output:$ pgbouncer --version
PgBouncer 1.18.0
libevent 2.1.12-stable
adns: c-ares 1.19.0
tls: OpenSSL 3.3.2 3 Sep 2024That way, you can get this information without having to start a server
instance. (Maybe you can't start a server instance because it just
crashed because of some library version issue ...)
While it is also useful to be able to get the info without being able to
start the server, I think that would be an addition not a replacement.
When you have a fleet with no direct access to run shell commands, being
able to get this info via SQL is valuable.
--
Joe Conway
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
Joe Conway <mail@joeconway.com> writes:
On 10/16/24 08:47, Peter Eisentraut wrote:
That way, you can get this information without having to start a server
instance. (Maybe you can't start a server instance because it just
crashed because of some library version issue ...)
While it is also useful to be able to get the info without being able to
start the server, I think that would be an addition not a replacement.
When you have a fleet with no direct access to run shell commands, being
able to get this info via SQL is valuable.
Yeah. In addition, I envisioned that this might include information
that's only readily available at runtime. Don't have a concrete
example at hand (-ENOCAFFEINE) but I think that --version is
necessarily going to be exceedingly constrained in what it can do.
Another problem is that, just like with version(), there is already
code making assumptions about what --version will output. Most of
that is under our control, but perhaps not all. The main value
of a new system view, IMV, is that it's a completely green field
for us to define the contents of.
regards, tom lane
On Mon, Oct 07, 2024 at 11:26:41AM GMT, Dmitry Dolgov wrote:
On Sun, Oct 06, 2024 at 12:01:29PM GMT, Joe Conway wrote:
I'm not sure why ICU is "Compile Time" rather than "Run Time" when it is not
statically linked.It reports U_UNICODE_VERSION at compile time. It's not necessarily
correct though, I can try to replace it with the runtime version. I
think there was some ICU functionality (something like
u_getUnicodeVersion), which is maybe a better fit.Also, if we are going to include ICU here, shouldn't we
also include libc version?Yeah, why not. One of my goals here is to identify a balanced set of
useful versions to report.
Here is how it would look like, I've added icu and glibc runtime
versions into the second patch.
Attachments:
v2-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From d903142d178dd8a9c03d07ee4809ac582b9b7818 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:27:57 +0200
Subject: [PATCH v2 1/4] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 56 ++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 108 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 40 +++++++++
src/test/regress/expected/rules.out | 8 ++
8 files changed, 229 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 634a4c0..df0b9d3 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -226,6 +226,11 @@
<entry>wait events</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5064,4 +5069,55 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-version">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about versions of various system components, e.g. PostgreSQL itself,
+ compiler used to build it, dependencies, etc.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 7fd5d25..091013d 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1377,3 +1377,11 @@ CREATE VIEW pg_stat_subscription_stats AS
CREATE VIEW pg_wait_events AS
SELECT * FROM pg_get_wait_events();
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index d9f5978..fcb3be7 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -31,7 +31,8 @@ OBJS = \
sampling.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 6669502..ca2abc5 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -15,6 +15,7 @@ backend_sources += files(
'rls.c',
'sampling.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 0000000..4d633fc
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,108 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unicode/uchar.h>
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char* name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+ ctl.hcxt = CurrentMemoryContext;
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicated system version")));
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char* version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = hentry->type;
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 43f608d..59587db 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12316,4 +12316,10 @@
proargtypes => 'int2',
prosrc => 'gist_stratnum_identity' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 0000000..a73f046
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#include <link.h>
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char* (*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index a1626f3..b9ad6f5 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2611,6 +2611,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
base-commit: 6aa44060a3c94ee10273bb8a89e98a5bb2fbbacb
--
2.45.1
v2-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From f3b2ea3dbea5aa8e9197a25d0f38141eb67f0163 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:30:34 +0200
Subject: [PATCH v2 2/4] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, glibc version, PostgreSQL itself and compiler
which was used to build everything. Register the core versions at the
backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Run Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
Glibc | 2.40 | Run Time
---
configure | 12 +++++
configure.ac | 4 ++
meson.build | 4 ++
src/backend/tcop/postgres.c | 12 +++++
src/backend/utils/misc/system_version.c | 59 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 ++
src/include/utils/system_version.h | 9 ++++
src/test/regress/expected/sysviews.out | 8 ++++
src/test/regress/sql/sysviews.sql | 4 ++
9 files changed, 116 insertions(+)
diff --git a/configure b/configure
index 53c8a1f..63e6d3e 100755
--- a/configure
+++ b/configure
@@ -19223,6 +19223,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index 6a35b28..d063504 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2417,6 +2417,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index 7150f85..c5dfba6 100644
--- a/meson.build
+++ b/meson.build
@@ -2756,6 +2756,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7f5eada..3b45fdc 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -80,6 +80,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -197,6 +198,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4369,6 +4371,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5282,3 +5287,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 4d633fc..8ff8697 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -67,6 +67,65 @@ add_system_version(const char* name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+ add_system_version("ICU", icu_get_version, RunTime);
+ add_system_version("Glibc", glibc_get_version, RunTime);
+}
+
+const char*
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_VERSION);
+}
+
+const char*
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_ARCH_STR);
+}
+
+const char*
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_CC_STR);
+}
+
+const char*
+icu_get_version(bool *available)
+{
+#ifdef USE_ICU
+ UVersionInfo UCDVersion;
+ char* version = palloc0(U_MAX_VERSION_STRING_LENGTH);
+
+ *available = true;
+ u_getUnicodeVersion(UCDVersion);
+ u_versionToString(UCDVersion, version);
+ return (const char*) version;
+#else
+ *available = false;
+ return (const char*) "";
+#endif
+}
+
+const char*
+glibc_get_version(bool *available)
+{
+ *available = true;
+ return (const char*) gnu_get_libc_version();
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3800636..b0a588b 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -621,6 +621,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index a73f046..8508b27 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -11,6 +11,7 @@
#ifndef SYSTEM_VERSION_H
#define SYSTEM_VERSION_H
+#include <gnu/libc-version.h>
#include <link.h>
#define MAX_SYSTEM_VERSIONS 100
@@ -36,5 +37,13 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
+
+const char* core_get_version(bool *available);
+const char* core_get_arch(bool *available);
+const char* core_get_compiler(bool *available);
+
+const char* icu_get_version(bool *available);
+const char* glibc_get_version(bool *available);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index fad7fc3..0afabc1 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -222,3 +222,11 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
t
(1 row)
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index b2a7923..7a5a5f6 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -98,3 +98,7 @@ set timezone_abbreviations = 'Australia';
select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
set timezone_abbreviations = 'India';
select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
+
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
--
2.45.1
v2-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 3644bb60a3c24ff0f488321a50f627c83c8a313d Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v2 3/4] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 19 +++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 17 +++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/include/jit/jit.h | 11 +++++++++++
src/include/jit/llvmjit.h | 2 ++
5 files changed, 56 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index 815b58f..8085a43 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,22 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Return JIT provider's version string for troubleshooting purposes.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ if (provider_init())
+ return provider.get_version(available);
+
+ *available = false;
+ return "";
+}
+
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 0f6cec5..2eed882 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -165,6 +165,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1382,3 +1383,19 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version(bool *available)
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major, minor, patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ *available = true;
+ return (const char*) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ *available = false;
+ return "";
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3b45fdc..84a2a44 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5293,4 +5293,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index d9a080c..6b3d453 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -13,6 +13,7 @@
#include "executor/instrument.h"
#include "utils/resowner.h"
+#include "utils/system_version.h"
/* Flags determining what kind of JIT operations to perform */
@@ -70,12 +71,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char* (*JitProviderVersion) (bool *available);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +105,13 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Get the provider's version string. The flag indicating availability is
+ * passed as an argument, and will be set accordingly if it's not possible to
+ * get the version.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 420775b..898848a 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -136,6 +136,8 @@ extern LLVMValueRef slot_compile_deform(struct LLVMJitContext *context, TupleDes
extern LLVMTypeRef LLVMGetFunctionReturnType(LLVMValueRef r);
extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
+extern const char* llvm_version(bool *available);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.45.1
v2-0004-Add-pg_system_libraries-view.patchtext/plain; charset=us-asciiDownload
From 2b8d560365012c06c54405bbfb7c69971d7e7dea Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:32:04 +0200
Subject: [PATCH v2 4/4] Add pg_system_libraries view
Introduce a way to report shared objects linked with PostgreSQL. Such
information is useful for troubleshooting, and could enhance bug reports. The
reporting is done via pg_system_libraries view, which contains a file path to
the shared object. It's implemented via standard C library dl_iterate_phdr,
which should be portable enough.
select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]
---
doc/src/sgml/system-views.sgml | 46 ++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/tcop/postgres.c | 1 +
src/backend/utils/misc/system_version.c | 81 +++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 9 +++
src/test/regress/expected/rules.out | 2 +
src/test/regress/expected/sysviews.out | 7 +++
src/test/regress/sql/sysviews.sql | 2 +
9 files changed, 157 insertions(+)
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index df0b9d3..bf397f5 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -231,6 +231,11 @@
<entry>system versions</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-libraries"><structname>pg_system_libraries</structname></link></entry>
+ <entry>linked libraries</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5120,4 +5125,45 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-libraries">
+ <title><structname>pg_system_libraries</structname></title>
+
+ <indexterm zone="view-pg-system-libraries">
+ <primary>pg_system_libraries</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_libraries</structname> provides description
+ about shared objects PostgreSQL is linked with.
+ </para>
+
+ <table>
+ <title><structname>pg_system_libraries</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Shared object file path
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 091013d..26f087c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1385,3 +1385,6 @@ CREATE VIEW pg_system_versions AS
WHEN 1 THEN 'Run Time'
END AS "type"
FROM pg_get_system_versions();
+
+CREATE VIEW pg_system_libraries AS
+ SELECT * FROM pg_get_system_libraries() as f(name text);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 84a2a44..80c5372 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4373,6 +4373,7 @@ PostgresMain(const char *dbname, const char *username)
/* Prepare information for reporting versions and libraries. */
register_system_versions();
+ register_libraries();
/*
* Also set up handler to log session end; we have to wait till now to be
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 8ff8697..7eb7138 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -14,6 +14,10 @@
* a version string directly allows for more flexibility about how and when the
* information could be reported.
*
+ * Libraries reporting is implemented similarly via a hash table containing
+ * only the library file path. This information is populated directly during
+ * the initialization.
+ *
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -32,6 +36,7 @@
#include "utils/system_version.h"
static HTAB *versions = NULL;
+static HTAB *libraries = NULL;
void
add_system_version(const char* name, SystemVersionCB cb, VersionType type)
@@ -165,3 +170,79 @@ pg_get_system_versions(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+
+/*
+ * Walk through list of shared objects and populate the libraries hash table.
+ */
+void
+register_libraries()
+{
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemLibrary);
+ ctl.hcxt = CurrentMemoryContext;
+
+ libraries = hash_create("Libraries table",
+ MAX_SYSTEM_LIBRARIES,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+
+ dl_iterate_phdr(add_library, NULL);
+}
+
+int add_library(struct dl_phdr_info *info, size_t size, void *data)
+{
+ const char *key;
+ bool found;
+
+ if (strcmp(info->dlpi_name, "") == 0)
+ {
+ /* The first visited object is the main program with the empty name,
+ * which is not so interesting. */
+ return 0;
+ }
+
+ key = pstrdup(info->dlpi_name);
+ hash_search(libraries, key, HASH_ENTER, &found);
+
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicated library")));
+
+ return 0;
+}
+
+/*
+ * pg_get_libraries
+ *
+ * List information about shared objects.
+ */
+Datum
+pg_get_system_libraries(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_LIBRARIES_COLS 1
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemLibrary *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, libraries);
+ while ((hentry = (SystemLibrary *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_LIBRARIES_COLS] = {0};
+ bool nulls[PG_GET_SYS_LIBRARIES_COLS] = {0};
+
+ values[0] = CStringGetTextDatum(hentry->filepath);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 59587db..f00a562 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12322,4 +12322,10 @@
proargtypes => '', proallargtypes => '{text,text,int8}',
proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
prosrc => 'pg_get_system_versions' },
+{ oid => '9433', descr => 'describe system libraries',
+ proname => 'pg_get_system_libraries', procost => '10', prorows => '30',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text}',
+ proargmodes => '{o}', proargnames => '{name}',
+ prosrc => 'pg_get_system_libraries' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 8508b27..e977d4f 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -15,6 +15,7 @@
#include <link.h>
#define MAX_SYSTEM_VERSIONS 100
+#define MAX_SYSTEM_LIBRARIES 100
typedef enum VersionType
{
@@ -36,8 +37,14 @@ typedef struct SystemVersion
SystemVersionCB callback; /* Callback to fetch the version string */
} SystemVersion;
+typedef struct SystemLibrary
+{
+ char filepath[NAMEDATALEN];
+} SystemLibrary;
+
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
extern void register_core_versions(void);
+extern void register_libraries(void);
const char* core_get_version(bool *available);
const char* core_get_arch(bool *available);
@@ -46,4 +53,6 @@ const char* core_get_compiler(bool *available);
const char* icu_get_version(bool *available);
const char* glibc_get_version(bool *available);
+int add_library(struct dl_phdr_info *info, size_t size, void *data);
+
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index b9ad6f5..3ba2510 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2611,6 +2611,8 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_libraries| SELECT name
+ FROM pg_get_system_libraries() f(name text);
pg_system_versions| SELECT name,
version,
CASE type
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 0afabc1..c8b14c1 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -230,3 +230,10 @@ select count(*) >= 4 as ok FROM pg_system_versions;
t
(1 row)
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 7a5a5f6..1334574 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -102,3 +102,5 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- At least 4 core versions should be present, architecture, ICU, core and
-- compiler
select count(*) >= 4 as ok FROM pg_system_versions;
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
--
2.45.1
hi.
https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F5318
shows lots of failures, but it doesn't seem to tell you about doc build failure.
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-version">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+ <indexterm zone="view-pg-system-version">
should change to
+ <indexterm zone="view-pg-system-versions">
otherwise cannot build doc.
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
...
column "type" of view pg_system_versions is missing in the doc entry
?
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("duplicated system version")));
this is unlikely to happen (not user visible error), normally we
should just use elog(ERROR...)
?
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
these two structs also need to be added into src/tools/pgindent/typedefs.list?
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -11,6 +11,7 @@
#ifndef SYSTEM_VERSION_H
#define SYSTEM_VERSION_H
+#include <gnu/libc-version.h>
#include <link.h>
"gnu/libc-version.h" does not exist in the clang compiler?
will "link.h" everywhere?
Currently, only a few rows are displayed for pg_system_versions. If I have
linked a dependency, I should be able to retrieve its version—for example, I
should be able to determine the version of zstd (i have linked the
zstd dependency).
On 2025-01-02 10:36:48 +0800, jian he wrote:
https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F5318
shows lots of failures, but it doesn't seem to tell you about doc build
failure.
It does:
https://cirrus-ci.com/task/6472750665039872?logs=docs_build#L0
[15:26:26.443] time make -s -j${BUILD_JOBS} -C doc
[15:26:28.759] postgres.sgml:5082: element indexterm: validity error : IDREFS attribute zone references an unknown ID "view-pg-system-version"
On Thu, Jan 02, 2025 at 10:36:48AM GMT, jian he wrote:
hi.
https://cirrus-ci.com/github/postgresql-cfbot/postgresql/cf%2F5318
shows lots of failures, but it doesn't seem to tell you about doc build failure.
Thanks for checking this out. Here is the updated version with applied
changes. The tests were failing due to injection_points library
apparently linking something twice, triggering a duplication error I
didn't expect. Since apparently it can happen, I'm just overwriting
duplicates.
--- a/src/include/utils/system_version.h +++ b/src/include/utils/system_version.h @@ -11,6 +11,7 @@ #ifndef SYSTEM_VERSION_H #define SYSTEM_VERSION_H+#include <gnu/libc-version.h>
#include <link.h>"gnu/libc-version.h" does not exist in the clang compiler?
will "link.h" everywhere?
This one I don't follow, what do you mean by "link.h" everywhere? I
think those two are coming from glibc and compiler independent. But it
indeed makes sense to wrap them into __GLIBC__.
Currently, only a few rows are displayed for pg_system_versions. If I have
linked a dependency, I should be able to retrieve its version—for example, I
should be able to determine the version of zstd (i have linked the
zstd dependency).
Maybe I'm missing something, but that's the plan -- pg_system_versions
contains only some selected components, that have to be registered
manually before showing up there. This of course means that it's hard to
get all the linked libraries, but opens a possibility to show any kind
of objects, not only what is linked. The actual list of versions is open
for discussion of course, but showing everything is not feasible I
think. For things like zstd pg_system_libraries is a better fit.
Attachments:
v3-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From fef34945c553de6db5abcd517f7c90fb9199e1b1 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 25 Jan 2025 20:25:11 +0100
Subject: [PATCH v3 1/4] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 65 +++++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 106 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 38 +++++++++
src/test/regress/expected/rules.out | 8 ++
src/tools/pgindent/typedefs.list | 2 +
9 files changed, 236 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 8e2b0a7927b..fa27f8cf52c 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -226,6 +226,11 @@
<entry>wait events</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5068,4 +5073,64 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-version">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about versions of various system components, e.g. PostgreSQL itself,
+ compiler used to build it, dependencies, etc.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>type</structfield> <type>text</type>
+ </para>
+ <para>
+ Component type (compile time or Run time)
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 46868bf7e89..0704567bf8c 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1388,3 +1388,11 @@ CREATE VIEW pg_stat_subscription_stats AS
CREATE VIEW pg_wait_events AS
SELECT * FROM pg_get_wait_events();
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index b362ae43771..a1bed7305a8 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -32,7 +32,8 @@ OBJS = \
stack_depth.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 9e389a00d05..5268eaa94c7 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -16,6 +16,7 @@ backend_sources += files(
'sampling.c',
'stack_depth.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 00000000000..ac7df0527fc
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,106 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unicode/uchar.h>
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char* name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+ ctl.hcxt = CurrentMemoryContext;
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ if (found)
+ elog(ERROR, "duplicated system version");
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char* version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = hentry->type;
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 2aafdbc3e93..563c2bc1437 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12438,4 +12438,10 @@
proargtypes => 'int4',
prosrc => 'gist_stratnum_common' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 00000000000..0321f67214f
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char* (*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 856a8349c50..047441f9ebb 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2617,6 +2617,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index a2644a2e653..ccad37e0e2c 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2823,6 +2823,7 @@ SysloggerStartupData
SystemRowsSamplerData
SystemSamplerData
SystemTimeSamplerData
+SystemVersion
TAPtype
TAR_MEMBER
TBMIterateResult
@@ -4064,6 +4065,7 @@ varattrib_1b_e
varattrib_4b
vbits
verifier_context
+VersionType
walrcv_alter_slot_fn
walrcv_check_conninfo_fn
walrcv_connect_fn
base-commit: 87a6690cc69530703b7da7e72769bae2ac5b2e77
--
2.45.1
v3-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 33e5e4b2245f3ba909321a6edcf20f042b17453a Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 25 Jan 2025 20:31:55 +0100
Subject: [PATCH v3 2/4] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, glibc version, PostgreSQL itself and compiler
which was used to build everything. Register the core versions at the
backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Run Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
Glibc | 2.40 | Run Time
---
configure | 12 +++++
configure.ac | 4 ++
meson.build | 4 ++
src/backend/tcop/postgres.c | 12 +++++
src/backend/utils/misc/system_version.c | 59 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 ++
src/include/utils/system_version.h | 12 +++++
src/test/regress/expected/sysviews.out | 8 ++++
src/test/regress/sql/sysviews.sql | 4 ++
9 files changed, 119 insertions(+)
diff --git a/configure b/configure
index ceeef9b0915..e68cb447f20 100755
--- a/configure
+++ b/configure
@@ -18715,6 +18715,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index d713360f340..07d1ca0cf9c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2352,6 +2352,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index 8e128f4982a..2eafd645399 100644
--- a/meson.build
+++ b/meson.build
@@ -2718,6 +2718,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5655348a2e2..15edc827d62 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -79,6 +79,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -184,6 +185,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4267,6 +4269,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5153,3 +5158,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index ac7df0527fc..d217c7e8cdb 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -65,6 +65,65 @@ add_system_version(const char* name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+ add_system_version("ICU", icu_get_version, RunTime);
+ add_system_version("Glibc", glibc_get_version, RunTime);
+}
+
+const char*
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_VERSION);
+}
+
+const char*
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_ARCH_STR);
+}
+
+const char*
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char*) psprintf("%s", PG_CC_STR);
+}
+
+const char*
+icu_get_version(bool *available)
+{
+#ifdef USE_ICU
+ UVersionInfo UCDVersion;
+ char* version = palloc0(U_MAX_VERSION_STRING_LENGTH);
+
+ *available = true;
+ u_getUnicodeVersion(UCDVersion);
+ u_versionToString(UCDVersion, version);
+ return (const char*) version;
+#else
+ *available = false;
+ return (const char*) "";
+#endif
+}
+
+const char*
+glibc_get_version(bool *available)
+{
+ *available = true;
+ return (const char*) gnu_get_libc_version();
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 07b2f798abd..1489ef7eeb1 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -591,6 +591,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 0321f67214f..28ebfb30807 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -11,6 +11,10 @@
#ifndef SYSTEM_VERSION_H
#define SYSTEM_VERSION_H
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
#define MAX_SYSTEM_VERSIONS 100
typedef enum VersionType
@@ -34,5 +38,13 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
+
+const char* core_get_version(bool *available);
+const char* core_get_arch(bool *available);
+const char* core_get_compiler(bool *available);
+
+const char* icu_get_version(bool *available);
+const char* glibc_get_version(bool *available);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 352abc0bd42..e9af9a72df6 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -231,3 +231,11 @@ select * from pg_timezone_abbrevs where abbrev = 'LMT';
LMT | @ 7 hours 52 mins 58 secs ago | f
(1 row)
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 66179f026b3..4ac0dacbb3e 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -101,3 +101,7 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- One specific case we can check without much fear of breakage
-- is the historical local-mean-time value used for America/Los_Angeles.
select * from pg_timezone_abbrevs where abbrev = 'LMT';
+
+-- At least 4 core versions should be present, architecture, ICU, core and
+-- compiler
+select count(*) >= 4 as ok FROM pg_system_versions;
--
2.45.1
v3-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From b3fd638e176a817eb110bcee419292d1303a12d9 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v3 3/4] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 19 +++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 17 +++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/include/jit/jit.h | 11 +++++++++++
src/include/jit/llvmjit.h | 2 ++
5 files changed, 56 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index d2ccef9de85..cd6505f0db0 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,22 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Return JIT provider's version string for troubleshooting purposes.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ if (provider_init())
+ return provider.get_version(available);
+
+ *available = false;
+ return "";
+}
+
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 614926720fb..ef8058e4af3 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -150,6 +150,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1275,3 +1276,19 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version(bool *available)
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major, minor, patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ *available = true;
+ return (const char*) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ *available = false;
+ return "";
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 15edc827d62..3d815bfa1d2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5164,4 +5164,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index 33cb36c5d2e..37776530acd 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -13,6 +13,7 @@
#include "executor/instrument.h"
#include "utils/resowner.h"
+#include "utils/system_version.h"
/* Flags determining what kind of JIT operations to perform */
@@ -70,12 +71,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char* (*JitProviderVersion) (bool *available);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +105,13 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Get the provider's version string. The flag indicating availability is
+ * passed as an argument, and will be set accordingly if it's not possible to
+ * get the version.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index 5038cf33e3f..7e3810f5965 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -144,6 +144,8 @@ extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
extern LLVMOrcObjectLayerRef LLVMOrcCreateRTDyldObjectLinkingLayerWithSafeSectionMemoryManager(LLVMOrcExecutionSessionRef ES);
#endif
+extern const char* llvm_version(bool *available);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.45.1
v3-0004-Add-pg_system_libraries-view.patchtext/plain; charset=us-asciiDownload
From 3c4d3b0b2f29dd65c4a01128f56babe552986800 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 25 Jan 2025 20:32:59 +0100
Subject: [PATCH v3 4/4] Add pg_system_libraries view
Introduce a way to report shared objects linked with PostgreSQL. Such
information is useful for troubleshooting, and could enhance bug reports. The
reporting is done via pg_system_libraries view, which contains a file path to
the shared object. It's implemented via standard C library dl_iterate_phdr,
which should be portable enough.
select * from pg_system_libraries;
name
-----------------------------
/lib64/libkrb5.so.3
/lib64/libz.so.1
linux-vdso.so.1
/lib64/libxml2.so.2
[...]
---
doc/src/sgml/system-views.sgml | 46 +++++++++++++++
src/backend/catalog/system_views.sql | 3 +
src/backend/tcop/postgres.c | 1 +
src/backend/utils/misc/system_version.c | 76 +++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 10 ++++
src/test/regress/expected/rules.out | 2 +
src/test/regress/expected/sysviews.out | 7 +++
src/test/regress/sql/sysviews.sql | 2 +
src/tools/pgindent/typedefs.list | 1 +
10 files changed, 154 insertions(+)
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index fa27f8cf52c..e86b1f61bbc 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -231,6 +231,11 @@
<entry>system versions</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-libraries"><structname>pg_system_libraries</structname></link></entry>
+ <entry>linked libraries</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5133,4 +5138,45 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-libraries">
+ <title><structname>pg_system_libraries</structname></title>
+
+ <indexterm zone="view-pg-system-libraries">
+ <primary>pg_system_libraries</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_libraries</structname> provides description
+ about shared objects PostgreSQL is linked with.
+ </para>
+
+ <table>
+ <title><structname>pg_system_libraries</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Shared object file path
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 0704567bf8c..46b8711be7e 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1396,3 +1396,6 @@ CREATE VIEW pg_system_versions AS
WHEN 1 THEN 'Run Time'
END AS "type"
FROM pg_get_system_versions();
+
+CREATE VIEW pg_system_libraries AS
+ SELECT * FROM pg_get_system_libraries() as f(name text);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3d815bfa1d2..602d0a51f2a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -4271,6 +4271,7 @@ PostgresMain(const char *dbname, const char *username)
/* Prepare information for reporting versions and libraries. */
register_system_versions();
+ register_libraries();
/*
* Also set up handler to log session end; we have to wait till now to be
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index d217c7e8cdb..0851da966f2 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -14,6 +14,10 @@
* a version string directly allows for more flexibility about how and when the
* information could be reported.
*
+ * Libraries reporting is implemented similarly via a hash table containing
+ * only the library file path. This information is populated directly during
+ * the initialization.
+ *
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
@@ -32,6 +36,7 @@
#include "utils/system_version.h"
static HTAB *versions = NULL;
+static HTAB *libraries = NULL;
void
add_system_version(const char* name, SystemVersionCB cb, VersionType type)
@@ -163,3 +168,74 @@ pg_get_system_versions(PG_FUNCTION_ARGS)
return (Datum) 0;
}
+
+/*
+ * Walk through list of shared objects and populate the libraries hash table.
+ */
+void
+register_libraries()
+{
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemLibrary);
+ ctl.hcxt = CurrentMemoryContext;
+
+ libraries = hash_create("Libraries table",
+ MAX_SYSTEM_LIBRARIES,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+
+ dl_iterate_phdr(add_library, NULL);
+}
+
+int add_library(struct dl_phdr_info *info, size_t size, void *data)
+{
+ const char *key;
+ bool found;
+
+ if (strcmp(info->dlpi_name, "") == 0)
+ {
+ /* The first visited object is the main program with the empty name,
+ * which is not so interesting. */
+ return 0;
+ }
+
+ key = pstrdup(info->dlpi_name);
+ hash_search(libraries, key, HASH_ENTER, &found);
+
+ return 0;
+}
+
+/*
+ * pg_get_libraries
+ *
+ * List information about shared objects.
+ */
+Datum
+pg_get_system_libraries(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_LIBRARIES_COLS 1
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemLibrary *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, libraries);
+ while ((hentry = (SystemLibrary *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_LIBRARIES_COLS] = {0};
+ bool nulls[PG_GET_SYS_LIBRARIES_COLS] = {0};
+
+ values[0] = CStringGetTextDatum(hentry->filepath);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 563c2bc1437..3584be8bf77 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12444,4 +12444,10 @@
proargtypes => '', proallargtypes => '{text,text,int8}',
proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
prosrc => 'pg_get_system_versions' },
+{ oid => '9433', descr => 'describe system libraries',
+ proname => 'pg_get_system_libraries', procost => '10', prorows => '30',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text}',
+ proargmodes => '{o}', proargnames => '{name}',
+ prosrc => 'pg_get_system_libraries' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 28ebfb30807..4c65e7288b2 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -13,9 +13,11 @@
#ifdef __GLIBC__
#include <gnu/libc-version.h>
+#include <link.h>
#endif
#define MAX_SYSTEM_VERSIONS 100
+#define MAX_SYSTEM_LIBRARIES 100
typedef enum VersionType
{
@@ -37,8 +39,14 @@ typedef struct SystemVersion
SystemVersionCB callback; /* Callback to fetch the version string */
} SystemVersion;
+typedef struct SystemLibrary
+{
+ char filepath[NAMEDATALEN];
+} SystemLibrary;
+
void add_system_version(const char* name, SystemVersionCB cb, VersionType type);
extern void register_core_versions(void);
+extern void register_libraries(void);
const char* core_get_version(bool *available);
const char* core_get_arch(bool *available);
@@ -47,4 +55,6 @@ const char* core_get_compiler(bool *available);
const char* icu_get_version(bool *available);
const char* glibc_get_version(bool *available);
+int add_library(struct dl_phdr_info *info, size_t size, void *data);
+
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 047441f9ebb..3949bebab3f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2617,6 +2617,8 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_libraries| SELECT name
+ FROM pg_get_system_libraries() f(name text);
pg_system_versions| SELECT name,
version,
CASE type
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index e9af9a72df6..af5b0c2ba5c 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -239,3 +239,10 @@ select count(*) >= 4 as ok FROM pg_system_versions;
t
(1 row)
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 4ac0dacbb3e..30000c3f770 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -105,3 +105,5 @@ select * from pg_timezone_abbrevs where abbrev = 'LMT';
-- At least 4 core versions should be present, architecture, ICU, core and
-- compiler
select count(*) >= 4 as ok FROM pg_system_versions;
+-- There is always some number of shared objects
+select count(*) > 0 as ok FROM pg_system_libraries;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index ccad37e0e2c..ae12644286a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2820,6 +2820,7 @@ SysFKRelationship
SysScanDesc
SyscacheCallbackFunction
SysloggerStartupData
+SystemLibrary
SystemRowsSamplerData
SystemSamplerData
SystemTimeSamplerData
--
2.45.1
Dmitry Dolgov <9erthalion6@gmail.com> writes:
Thanks for checking this out. Here is the updated version with applied
changes. The tests were failing due to injection_points library
apparently linking something twice, triggering a duplication error I
didn't expect. Since apparently it can happen, I'm just overwriting
duplicates.
FWIW, I think the 0004 patch is about to be mostly obsoleted by
Andrei's proposal at [1]/messages/by-id/dd4d1b59-d0fe-49d5-b28f-1e463b68fa32@gmail.com. To the extent that it's not obsoleted,
I question whether it's something we want at all, given the ground
rule that unprivileged users are not supposed to have access to info
about the server's filesystem.
regards, tom lane
[1]: /messages/by-id/dd4d1b59-d0fe-49d5-b28f-1e463b68fa32@gmail.com
On Sun, Mar 23, 2025 at 06:21:33PM GMT, Tom Lane wrote:
FWIW, I think the 0004 patch is about to be mostly obsoleted by
Andrei's proposal at [1]. To the extent that it's not obsoleted,
I question whether it's something we want at all, given the ground
rule that unprivileged users are not supposed to have access to info
about the server's filesystem.
To be clear -- I don't have a case for 0004 myself, except some vague
expectation that in certain situations it could be useful to know which
shared objects are loaded, even if they are not Postgres modules. Based
on the feedback from the original thread [2]/messages/by-id/znc72ymyoelvk5rjk5ub254v3qvcczfrk6autygjdobfvx2e7p@s3dssvf34twa, there were couple similar
opinions, maybe folks could reply here whether [1] would be sufficient
for them.
I agree with the argument about the privileges. If the 0004 patch will
be found useful, it would make sense to allow only superuser to access
this view. I assume "revoke all on pg_system_libraries from public"
should be enough, would this address the concern?
[2]: /messages/by-id/znc72ymyoelvk5rjk5ub254v3qvcczfrk6autygjdobfvx2e7p@s3dssvf34twa
On Tue, Apr 01, 2025 at 09:27:23PM +0200, Dmitry Dolgov wrote:
On Sun, Mar 23, 2025 at 06:21:33PM GMT, Tom Lane wrote:
FWIW, I think the 0004 patch is about to be mostly obsoleted by
Andrei's proposal at [1]. To the extent that it's not obsoleted,
I question whether it's something we want at all, given the ground
rule that unprivileged users are not supposed to have access to info
about the server's filesystem.To be clear -- I don't have a case for 0004 myself, except some vague
expectation that in certain situations it could be useful to know which
shared objects are loaded, even if they are not Postgres modules. Based
on the feedback from the original thread [2], there were couple similar
opinions, maybe folks could reply here whether [1] would be sufficient
for them.
Better late than never, rebased and dropped 0004.
Attachments:
v4-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From efae8acde7be2a504dbcccfc43f7994c94753c23 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sun, 16 Nov 2025 16:01:21 +0100
Subject: [PATCH v4 1/3] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 65 +++++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 106 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 38 +++++++++
src/test/regress/expected/rules.out | 8 ++
src/tools/pgindent/typedefs.list | 2 +
9 files changed, 236 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 7971498fe75..cb7b57e0102 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -246,6 +246,11 @@
<entry>wait events</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
@@ -5611,4 +5616,64 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</table>
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-version">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about versions of various system components, e.g. PostgreSQL itself,
+ compiler used to build it, dependencies, etc.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>type</structfield> <type>text</type>
+ </para>
+ <para>
+ Component type (compile time or Run time)
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
</chapter>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 059e8778ca7..a00777e4f8b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1438,3 +1438,11 @@ REVOKE ALL ON pg_aios FROM PUBLIC;
GRANT SELECT ON pg_aios TO pg_read_all_stats;
REVOKE EXECUTE ON FUNCTION pg_get_aios() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION pg_get_aios() TO pg_read_all_stats;
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index f142d17178b..0ac5b3c79cd 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -32,7 +32,8 @@ OBJS = \
stack_depth.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 9e389a00d05..5268eaa94c7 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -16,6 +16,7 @@ backend_sources += files(
'sampling.c',
'stack_depth.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 00000000000..4d15ce58bf9
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,106 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unicode/uchar.h>
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char *name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+ ctl.hcxt = CurrentMemoryContext;
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ if (found)
+ elog(ERROR, "duplicated system version");
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char *version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = hentry->type;
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5cf9e12fcb9..858e2f9983b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12604,4 +12604,10 @@
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' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 00000000000..18cb673d4ca
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char *(*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7c52181cbcb..7db23198ff3 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2683,6 +2683,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 23bce72ae64..5ebb83d9899 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2934,6 +2934,7 @@ SysloggerStartupData
SystemRowsSamplerData
SystemSamplerData
SystemTimeSamplerData
+SystemVersion
TAPtype
TAR_MEMBER
TBMIterateResult
@@ -4215,6 +4216,7 @@ varattrib_1b_e
varattrib_4b
vbits
verifier_context
+VersionType
walrcv_alter_slot_fn
walrcv_check_conninfo_fn
walrcv_connect_fn
--
2.49.0
v4-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 71113edfe4e9571e2bf0fe59554913331560754d Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sun, 16 Nov 2025 17:30:59 +0100
Subject: [PATCH v4 2/3] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, glibc version, PostgreSQL itself and compiler
which was used to build everything. Register the core versions at the
backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Run Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
Glibc | 2.40 | Run Time
---
configure | 12 +++++
configure.ac | 4 ++
meson.build | 4 ++
src/backend/tcop/postgres.c | 12 +++++
src/backend/utils/misc/system_version.c | 59 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 ++
src/include/utils/system_version.h | 12 +++++
src/test/regress/expected/sysviews.out | 9 ++++
src/test/regress/sql/sysviews.sql | 5 +++
9 files changed, 121 insertions(+)
diff --git a/configure b/configure
index 3a0ed11fa8e..f7d5f695679 100755
--- a/configure
+++ b/configure
@@ -19293,6 +19293,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index c2413720a18..9f7f7f75535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2448,6 +2448,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index c1e17aa3040..2265eaafb48 100644
--- a/meson.build
+++ b/meson.build
@@ -2972,6 +2972,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7dd75a490aa..3a8ff419425 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -81,6 +81,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -186,6 +187,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4318,6 +4320,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5237,3 +5242,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 4d15ce58bf9..6d86b90d79c 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -65,6 +65,65 @@ add_system_version(const char *name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+ add_system_version("ICU", icu_get_version, RunTime);
+ add_system_version("Glibc", glibc_get_version, RunTime);
+}
+
+const char *
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_VERSION);
+}
+
+const char *
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_ARCH_STR);
+}
+
+const char *
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_CC_STR);
+}
+
+const char *
+icu_get_version(bool *available)
+{
+#ifdef USE_ICU
+ UVersionInfo UCDVersion;
+ char *version = palloc0(U_MAX_VERSION_STRING_LENGTH);
+
+ *available = true;
+ u_getUnicodeVersion(UCDVersion);
+ u_versionToString(UCDVersion, version);
+ return (const char *) version;
+#else
+ *available = false;
+ return (const char *) "";
+#endif
+}
+
+const char *
+glibc_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) gnu_get_libc_version();
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..dacf496ce71 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -621,6 +621,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 18cb673d4ca..f9680527d55 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -11,6 +11,10 @@
#ifndef SYSTEM_VERSION_H
#define SYSTEM_VERSION_H
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
#define MAX_SYSTEM_VERSIONS 100
typedef enum VersionType
@@ -34,5 +38,13 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
+
+const char *core_get_version(bool *available);
+const char *core_get_arch(bool *available);
+const char *core_get_compiler(bool *available);
+
+const char *icu_get_version(bool *available);
+const char *glibc_get_version(bool *available);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 3b37fafa65b..c813ef10eb5 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -233,3 +233,12 @@ select * from pg_timezone_abbrevs where abbrev = 'LMT';
LMT | @ 7 hours 52 mins 58 secs ago | f
(1 row)
+-- 5 core versions should be present: architecture, ICU, core, compiler and
+-- glibc. If built with JIT support, one more record will be displayed
+-- containing LLVM version.
+select count(*) >= 5 as ok FROM pg_system_versions;
+ ok
+----
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 66179f026b3..798a6dddd57 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -101,3 +101,8 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- One specific case we can check without much fear of breakage
-- is the historical local-mean-time value used for America/Los_Angeles.
select * from pg_timezone_abbrevs where abbrev = 'LMT';
+
+-- 5 core versions should be present: architecture, ICU, core, compiler and
+-- glibc. If built with JIT support, one more record will be displayed
+-- containing LLVM version.
+select count(*) >= 5 as ok FROM pg_system_versions;
--
2.49.0
v4-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 7b71bc3ba1dada57dab8cab31a2911256ccf49a0 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v4 3/3] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 19 +++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 19 +++++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/include/jit/jit.h | 11 +++++++++++
src/include/jit/llvmjit.h | 2 ++
5 files changed, 58 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index d2ccef9de85..cd6505f0db0 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,22 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Return JIT provider's version string for troubleshooting purposes.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ if (provider_init())
+ return provider.get_version(available);
+
+ *available = false;
+ return "";
+}
+
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index e978b996bae..9b40af60464 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -154,6 +154,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1280,3 +1281,21 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version(bool *available)
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major,
+ minor,
+ patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ *available = true;
+ return (const char *) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ *available = false;
+ return "";
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3a8ff419425..a23a858f2c9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5248,4 +5248,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index 33cb36c5d2e..8088ab37256 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -13,6 +13,7 @@
#include "executor/instrument.h"
#include "utils/resowner.h"
+#include "utils/system_version.h"
/* Flags determining what kind of JIT operations to perform */
@@ -70,12 +71,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char *(*JitProviderVersion) (bool *available);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +105,13 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Get the provider's version string. The flag indicating availability is
+ * passed as an argument, and will be set accordingly if it's not possible to
+ * get the version.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index b3c75022f55..1125451fbf5 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -145,6 +145,8 @@ extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
extern LLVMOrcObjectLayerRef LLVMOrcCreateRTDyldObjectLinkingLayerWithSafeSectionMemoryManager(LLVMOrcExecutionSessionRef ES);
#endif
+extern const char* llvm_version(bool *available);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.49.0
On Sun, 2025-11-16 at 17:45 +0100, Dmitry Dolgov wrote:
Better late than never, rebased and dropped 0004.
I think that having a system view like that would be quite useful.
It would be even more useful if it included other libraries that are
optionally linked with PostgreSQL, like OpenSSL, OpenLDAP, lz4,
zstd, GSSAPI etc.
Building the server worked fine on my Linux box (but see my comments
about Glibc below), and the view behaves like it should.
Building the documentation fails:
element indexterm: validity error : IDREFS attribute zone references an unknown ID "view-pg-system-version"
The problem is here:
--- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml + <sect1 id="view-pg-system-versions"> + <title><structname>pg_system_versions</structname></title> + + <indexterm zone="view-pg-system-version"> + <primary>pg_system_versions</primary> + </indexterm>
"view-pg-system-version" is missing the "s" at the end.
Comments on the code:
=====================
Patch 0001:
-----------
--- a/doc/src/sgml/system-views.sgml +++ b/doc/src/sgml/system-views.sgml + <row> + <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry> + <entry>system versions</entry> + </row> + </tbody> </tgroup> </table>
That list is alphabetically sorted by function name. You should add the function at the
correct place, not at the end.
The same applies to the next hunk. It gets rendered in its own page, so the order is
not user-visible, but keeping these sections in the same alphabetical order will ease
maintenance.
+ <sect1 id="view-pg-system-versions"> + <title><structname>pg_system_versions</structname></title> + + <indexterm zone="view-pg-system-version"> + <primary>pg_system_versions</primary> + </indexterm> + + <para> + The view <structname>pg_system_versions</structname> provides description + about versions of various system components, e.g. PostgreSQL itself, + compiler used to build it, dependencies, etc. + </para> + [...] + <row> + <entry role="catalog_table_entry"><para role="column_definition"> + <structfield>type</structfield> <type>text</type> + </para> + <para> + Component type (compile time or Run time) + </para></entry> + </row>
I'm not happy with the English in "The view ... provides description about ...",
but all the other views use the same wording, so we should stick with it.
But in "description about versions of various system components", there should
be a "the" before "versions". It should also be "the compiler". Since you start
the list with "e.g.", I think the "etc." is superfluous.
In "compile time or Run time", either use upper case everywhere (except in "or")
or lower case everywhere. I suggest to use upper case like in the actual view
output.
--- /dev/null +++ b/src/backend/utils/misc/system_version.c +void +add_system_version(const char *name, SystemVersionCB cb, VersionType type) +{ + SystemVersion *hentry; + const char *key; + bool found; + + if (!versions) + { + HASHCTL ctl; + + ctl.keysize = NAMEDATALEN; + ctl.entrysize = sizeof(SystemVersion); + ctl.hcxt = CurrentMemoryContext;
That hashtable should stick around for the entire life time of the backend.
Wouldn't it be better to explicitly name the correct memory context, rather
that to rely on being called with "CurrentMemoryContext" set correctly?
+ + versions = hash_create("System versions table", + MAX_SYSTEM_VERSIONS, + &ctl, + HASH_ELEM | HASH_STRINGS); + } + + key = pstrdup(name); + hentry = (SystemVersion *) hash_search(versions, key, + HASH_ENTER, &found);
I think you should at least add an Assert() that makes sure that the key
is not longer than NAMEDATALEN.
+ + if (found) + elog(ERROR, "duplicated system version");
Is that a problem? Why throw an error?
+Datum +pg_get_system_versions(PG_FUNCTION_ARGS) +{ [...] + while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL) + { + Datum values[PG_GET_SYS_VERSIONS_COLS] = {0}; + bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0}; + bool available = false; + const char *version = hentry->callback(&available); + + if (!available) + continue; + + values[0] = CStringGetTextDatum(hentry->name); + values[1] = CStringGetTextDatum(version); + values[2] = hentry->type; + + tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); + }
I think the assignment for the third column should be
values[2] = Int32GetDatum((int32) hentry->type);
since hentry->type is an enum type and hence an "int".
Patch 0002:
-----------
--- a/src/backend/utils/misc/system_version.c +++ b/src/backend/utils/misc/system_version.c +/* + * Register versions that describe core components and do not correspond to any + * individual component. + */ +void +register_core_versions() +{ + add_system_version("Core", core_get_version, CompileTime); + add_system_version("Arch", core_get_arch, CompileTime); + add_system_version("Compiler", core_get_compiler, CompileTime); + add_system_version("ICU", icu_get_version, RunTime); + add_system_version("Glibc", glibc_get_version, RunTime); +} [...] +const char * +glibc_get_version(bool *available) +{ + *available = true; + return (const char *) gnu_get_libc_version(); +}
Not all operating systems use Glibc, not even all Linux distributions.
If you look at the commitfest entry, you'll see the the CI build fails on
most platforms.
At the very least, this calls for conditional compilation.
Really, there should be an add_system_version() call for all types of
C libraries that exist out there, but that might border on the impossible.
--- a/src/include/utils/system_version.h +++ b/src/include/utils/system_version.h @@ -11,6 +11,10 @@ #ifndef SYSTEM_VERSION_H #define SYSTEM_VERSION_H+#ifdef __GLIBC__ +#include <gnu/libc-version.h> +#endif +
That should be included where glibc_get_version() is defined, not here.
#define MAX_SYSTEM_VERSIONS 100
typedef enum VersionType
@@ -34,5 +38,13 @@ typedef struct SystemVersion
} SystemVersion;void add_system_version(const char *name, SystemVersionCB cb, VersionType type); +extern void register_core_versions(void); + +const char *core_get_version(bool *available); +const char *core_get_arch(bool *available); +const char *core_get_compiler(bool *available); + +const char *icu_get_version(bool *available); +const char *glibc_get_version(bool *available);#endif /* SYSTEM_VERSION_H */
I think that these functions had better be "static". Do you expect a need to
call them from other parts of the code?
--- a/src/test/regress/sql/sysviews.sql +++ b/src/test/regress/sql/sysviews.sql @@ -101,3 +101,8 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; -- One specific case we can check without much fear of breakage -- is the historical local-mean-time value used for America/Los_Angeles. select * from pg_timezone_abbrevs where abbrev = 'LMT'; + +-- 5 core versions should be present: architecture, ICU, core, compiler and +-- glibc. If built with JIT support, one more record will be displayed +-- containing LLVM version. +select count(*) >= 5 as ok FROM pg_system_versions;
If you really think that a regression test for selecting from the view
is useful, use "SELECT EXISTS (TABLE pg_system_versions)".
Are you sure that the regression tests will always run with ICU enabled?
Patch 0003:
-----------
--- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c + +/* + * Return JIT provider's version string for troubleshooting purposes. + */ +const char * +jit_get_version(bool *available) +{ + if (provider_init()) + return provider.get_version(available); + + *available = false; + return ""; +}
I think it would be better to do do the "available" dance here rather than
delegating it to get_version(). get_version() can simply return NULL if there
is no version available.
A more meaningful function comment would be "Callback for add_system_version()".
+void +jit_register_version(void) +{ + add_system_version("LLVM", jit_get_version, RunTime); +}
I think that should be in utils/misc/system_version.c.
Then you don't need to #include "utils/system_version.h" in the JIT code.
--- a/src/include/jit/jit.h +++ b/src/include/jit/jit.h +/* + * Get the provider's version string. The flag indicating availability is + * passed as an argument, and will be set accordingly if it's not possible to + * get the version. + */ +extern const char *jit_get_version(bool *available);
Again, I think "Callback for add_system_version()" would be a more useful
comment. I have no trouble guessing that the function will return the JIT
version, and if you know add_system_version(), you know what "available" is.
Yours,
Laurenz Albe
Thanks for the detailed review! I generally agree with most of the
points and will try to incorporate them into the next version. Below are
few commentaries / questions for the rest.
On Wed, Nov 19, 2025 at 09:57:12PM +0100, Laurenz Albe wrote:
On Sun, 2025-11-16 at 17:45 +0100, Dmitry Dolgov wrote:Better late than never, rebased and dropped 0004.
I think that having a system view like that would be quite useful.
It would be even more useful if it included other libraries that are
optionally linked with PostgreSQL, like OpenSSL, OpenLDAP, lz4,
zstd, GSSAPI etc.
Yeah, my plan is to extend the list of versions as soon as the
infrastructure is sorted out.
--- /dev/null +++ b/src/backend/utils/misc/system_version.c +void +add_system_version(const char *name, SystemVersionCB cb, VersionType type) +{ + SystemVersion *hentry; + const char *key; + bool found; + + if (!versions) + { + HASHCTL ctl; + + ctl.keysize = NAMEDATALEN; + ctl.entrysize = sizeof(SystemVersion); + ctl.hcxt = CurrentMemoryContext;That hashtable should stick around for the entire life time of the backend.
Wouldn't it be better to explicitly name the correct memory context, rather
that to rely on being called with "CurrentMemoryContext" set correctly?
Looking at this closely, I think it's not even necessary to specify a
memory context here -- in this case hash_create will create a private
memory context for this hash table from TopMemoryContext.
+ + if (found) + elog(ERROR, "duplicated system version");Is that a problem? Why throw an error?
It was originally a problem for 0004, and propagated here as well. But I
still see some value in it, because I can't imagine why registering the
same version twice might be a reasonable use cases. Do you have some
examples for such cases in mind?
--- a/src/test/regress/sql/sysviews.sql +++ b/src/test/regress/sql/sysviews.sql @@ -101,3 +101,8 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs; -- One specific case we can check without much fear of breakage -- is the historical local-mean-time value used for America/Los_Angeles. select * from pg_timezone_abbrevs where abbrev = 'LMT'; + +-- 5 core versions should be present: architecture, ICU, core, compiler and +-- glibc. If built with JIT support, one more record will be displayed +-- containing LLVM version. +select count(*) >= 5 as ok FROM pg_system_versions;If you really think that a regression test for selecting from the view
is useful, use "SELECT EXISTS (TABLE pg_system_versions)".
Are you sure that the regression tests will always run with ICU enabled?
Not with ICU, but at least the core and compiler version are always
going to be present, hence the check for number of records. Any
alternative suggestions for better testing coverage?
--- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c + +/* + * Return JIT provider's version string for troubleshooting purposes. + */ +const char * +jit_get_version(bool *available) +{ + if (provider_init()) + return provider.get_version(available); + + *available = false; + return ""; +}I think it would be better to do do the "available" dance here rather than
delegating it to get_version(). get_version() can simply return NULL if there
is no version available.
I don't have any preferences here and can move it. But just out of
curiosity, can you elaborate what benefits do you see in moving the
"available" logic in jit_get_version?
On Fri, 2025-11-21 at 15:21 +0100, Dmitry Dolgov wrote:
+ ctl.hcxt = CurrentMemoryContext;
That hashtable should stick around for the entire life time of the backend.
Wouldn't it be better to explicitly name the correct memory context, rather
that to rely on being called with "CurrentMemoryContext" set correctly?Looking at this closely, I think it's not even necessary to specify a
memory context here -- in this case hash_create will create a private
memory context for this hash table from TopMemoryContext.
That is fine. I just think that using CurrentMemoryContext a) opens a window
for mistakes if you call the function from the wrong place and b) makes it
less clear to the reader where the hash table will be created.
+ if (found) + elog(ERROR, "duplicated system version");Is that a problem? Why throw an error?
It was originally a problem for 0004, and propagated here as well. But I
still see some value in it, because I can't imagine why registering the
same version twice might be a reasonable use cases. Do you have some
examples for such cases in mind?
No, I don't think that there are any use cases. But registering the same
version twice will just overwrite the old value, which is redundant, but no
problem. It would be bad coding though, so perhaps an Assert() would be the
better choice. That would avoid the (tiny) overhead of the check in
production builds.
--- a/src/test/regress/sql/sysviews.sql +++ b/src/test/regress/sql/sysviews.sql + +-- 5 core versions should be present: architecture, ICU, core, compiler and +-- glibc. If built with JIT support, one more record will be displayed +-- containing LLVM version. +select count(*) >= 5 as ok FROM pg_system_versions;If you really think that a regression test for selecting from the view
is useful, use "SELECT EXISTS (TABLE pg_system_versions)".
Are you sure that the regression tests will always run with ICU enabled?Not with ICU, but at least the core and compiler version are always
going to be present, hence the check for number of records. Any
alternative suggestions for better testing coverage?
I made my suggestion in the paragraph above. I think it is sufficient to check
that you can select from the view at all, never mind how many columns there are.
--- a/src/backend/jit/jit.c +++ b/src/backend/jit/jit.c + +/* + * Return JIT provider's version string for troubleshooting purposes. + */ +const char * +jit_get_version(bool *available) +{ + if (provider_init()) + return provider.get_version(available); + + *available = false; + return ""; +}I think it would be better to do do the "available" dance here rather than
delegating it to get_version(). get_version() can simply return NULL if there
is no version available.I don't have any preferences here and can move it. But just out of
curiosity, can you elaborate what benefits do you see in moving the
"available" logic in jit_get_version?
I don't have a hard technical argument for that. My gut feeling is the following:
jit_get_version() belongs to the pg_system_versions machinery, so it should deal
with the "available" that belongs to that API. jit_get_version() belongs to the
JIT implementation.
I won't object if you feel strongly that it should be the way it is now, since
that is more a matter of taste than anything else.
Yours,
Laurenz Albe
On Fri, Nov 21, 2025 at 03:21:45PM +0100, Dmitry Dolgov wrote:
Thanks for the detailed review! I generally agree with most of the
points and will try to incorporate them into the next version. Below are
few commentaries / questions for the rest.
Here is the updated patch.
Attachments:
v5-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From cc30fe6ec5cb5219c994b2cdd399c6b08b6981ca Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sun, 16 Nov 2025 16:01:21 +0100
Subject: [PATCH v5 1/3] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 65 ++++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 107 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 38 +++++++++
src/test/regress/expected/rules.out | 8 ++
src/tools/pgindent/typedefs.list | 2 +
9 files changed, 237 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 7971498fe75..dbdba3a5576 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -211,6 +211,11 @@
<entry>extended planner statistics for expressions</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
<row>
<entry><link linkend="view-pg-tables"><structname>pg_tables</structname></link></entry>
<entry>tables</entry>
@@ -4986,6 +4991,66 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-versions">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about versions of various system components, e.g. PostgreSQL itself,
+ compiler used to build it, dependencies, etc.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>type</structfield> <type>text</type>
+ </para>
+ <para>
+ Component type (compile time or Run time)
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="view-pg-tables">
<title><structname>pg_tables</structname></title>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 059e8778ca7..a00777e4f8b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1438,3 +1438,11 @@ REVOKE ALL ON pg_aios FROM PUBLIC;
GRANT SELECT ON pg_aios TO pg_read_all_stats;
REVOKE EXECUTE ON FUNCTION pg_get_aios() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION pg_get_aios() TO pg_read_all_stats;
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index f142d17178b..0ac5b3c79cd 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -32,7 +32,8 @@ OBJS = \
stack_depth.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 9e389a00d05..5268eaa94c7 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -16,6 +16,7 @@ backend_sources += files(
'sampling.c',
'stack_depth.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 00000000000..6f3445b0539
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,107 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <unicode/uchar.h>
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char *name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ Assert(strlen(key) < NAMEDATALEN);
+
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ /* Duplicated entries are not expected */
+ Assert(!found);
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char *version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = Int32GetDatum(hentry->type);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5cf9e12fcb9..858e2f9983b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12604,4 +12604,10 @@
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' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 00000000000..18cb673d4ca
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char *(*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7c52181cbcb..7db23198ff3 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2683,6 +2683,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 23bce72ae64..5ebb83d9899 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2934,6 +2934,7 @@ SysloggerStartupData
SystemRowsSamplerData
SystemSamplerData
SystemTimeSamplerData
+SystemVersion
TAPtype
TAR_MEMBER
TBMIterateResult
@@ -4215,6 +4216,7 @@ varattrib_1b_e
varattrib_4b
vbits
verifier_context
+VersionType
walrcv_alter_slot_fn
walrcv_check_conninfo_fn
walrcv_connect_fn
--
2.49.0
v5-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 55636d118868bc28e391be668377264ac86f28d7 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sun, 16 Nov 2025 17:30:59 +0100
Subject: [PATCH v5 2/3] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, glibc version, PostgreSQL itself and compiler
which was used to build everything. Register the core versions at the
backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Run Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
Glibc | 2.40 | Run Time
---
configure | 12 ++++
configure.ac | 4 ++
meson.build | 4 ++
src/backend/tcop/postgres.c | 12 ++++
src/backend/utils/misc/system_version.c | 74 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 ++
src/include/utils/system_version.h | 1 +
src/test/regress/expected/sysviews.out | 10 ++++
src/test/regress/sql/sysviews.sql | 6 ++
9 files changed, 127 insertions(+)
diff --git a/configure b/configure
index 3a0ed11fa8e..f7d5f695679 100755
--- a/configure
+++ b/configure
@@ -19293,6 +19293,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index c2413720a18..9f7f7f75535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2448,6 +2448,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index c1e17aa3040..2265eaafb48 100644
--- a/meson.build
+++ b/meson.build
@@ -2972,6 +2972,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7dd75a490aa..3a8ff419425 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -81,6 +81,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -186,6 +187,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4318,6 +4320,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5237,3 +5242,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 6f3445b0539..752546b8e7a 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -27,6 +27,10 @@
#include <unicode/uchar.h>
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/system_version.h"
@@ -66,6 +70,76 @@ add_system_version(const char *name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+static const char *
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_VERSION);
+}
+
+static const char *
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_ARCH_STR);
+}
+
+static const char *
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_CC_STR);
+}
+
+static const char *
+icu_get_version(bool *available)
+{
+#ifdef USE_ICU
+ UVersionInfo UCDVersion;
+ char *version = palloc0(U_MAX_VERSION_STRING_LENGTH);
+
+ *available = true;
+ u_getUnicodeVersion(UCDVersion);
+ u_versionToString(UCDVersion, version);
+ return (const char *) version;
+#else
+ *available = false;
+ return (const char *) "";
+#endif
+}
+
+#ifdef __GLIBC__
+static const char *
+glibc_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) gnu_get_libc_version();
+}
+#endif
+
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
+
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+ add_system_version("ICU", icu_get_version, RunTime);
+
+#ifdef __GLIBC__
+ add_system_version("Glibc", glibc_get_version, RunTime);
+#endif
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..dacf496ce71 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -621,6 +621,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 18cb673d4ca..49685b6809f 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -34,5 +34,6 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 3b37fafa65b..90c2fdeba2d 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -233,3 +233,13 @@ select * from pg_timezone_abbrevs where abbrev = 'LMT';
LMT | @ 7 hours 52 mins 58 secs ago | f
(1 row)
+-- Check that the pg_system_versions contains something. Three core versions
+-- should be present: architecture, core and compiler and glibc. More records
+-- may be present depending on the build configuration (e.g. with ICU, GlibC or
+-- JIT support).
+select exists (table pg_system_versions);
+ exists
+--------
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 66179f026b3..c6328ec3d57 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -101,3 +101,9 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- One specific case we can check without much fear of breakage
-- is the historical local-mean-time value used for America/Los_Angeles.
select * from pg_timezone_abbrevs where abbrev = 'LMT';
+
+-- Check that the pg_system_versions contains something. Three core versions
+-- should be present: architecture, core and compiler and glibc. More records
+-- may be present depending on the build configuration (e.g. with ICU, GlibC or
+-- JIT support).
+select exists (table pg_system_versions);
--
2.49.0
v5-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 1b48f779790d722c46ddb0f2d495ef39ec911275 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v5 3/3] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 27 +++++++++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 17 ++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/backend/utils/misc/system_version.c | 1 +
src/include/jit/jit.h | 9 +++++++++
src/include/jit/llvmjit.h | 2 ++
6 files changed, 63 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index d2ccef9de85..f4bd0b768ad 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,30 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Callback for add_system_version, returns JIT provider's version string and
+ * reports if it's not available.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ const char *version;
+
+ if (!provider_init())
+ {
+ *available = false;
+ return "";
+ }
+
+ version = provider.get_version();
+
+ if (version == NULL)
+ {
+ *available = false;
+ return "";
+ }
+
+ *available = true;
+ return version;
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index e978b996bae..b2f445963ab 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -154,6 +154,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1280,3 +1281,19 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version()
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major,
+ minor,
+ patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ return (const char *) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ return NULL;
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3a8ff419425..a23a858f2c9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5248,4 +5248,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index 752546b8e7a..145ec36182d 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -32,6 +32,7 @@
#endif
#include "funcapi.h"
+#include "jit/jit.h"
#include "utils/builtins.h"
#include "utils/system_version.h"
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index 33cb36c5d2e..5342a282fb4 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -70,12 +70,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char *(*JitProviderVersion) (void);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +104,12 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Callback for add_system_version, get the provider's version string. The flag
+ * indicating availability is passed as an argument.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index b3c75022f55..1c903a4dc52 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -145,6 +145,8 @@ extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
extern LLVMOrcObjectLayerRef LLVMOrcCreateRTDyldObjectLinkingLayerWithSafeSectionMemoryManager(LLVMOrcExecutionSessionRef ES);
#endif
+extern const char* llvm_version(void);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.49.0
On Tue, Nov 25, 2025 at 04:40:52PM +0100, Dmitry Dolgov wrote:
On Fri, Nov 21, 2025 at 03:21:45PM +0100, Dmitry Dolgov wrote:
Thanks for the detailed review! I generally agree with most of the
points and will try to incorporate them into the next version. Below are
few commentaries / questions for the rest.Here is the updated patch.
Looks line MinGW and Visual Studio lack unicode/uchar.h, I would need to
add something for them -- will follow up shortly.
On Tue, 2025-11-25 at 16:40 +0100, Dmitry Dolgov wrote:
Here is the updated patch.
Thanks for the updated patch.
Comments:
You didn't address any of my suggestions concerning the documentation,
except that you moved the entry in "System Views" to the correct place.
The second patch contains:
+void +jit_register_version(void) +{ + add_system_version("LLVM", jit_get_version, RunTime); +}
But that belongs into the third patch.
+/*
+ * Callback for add_system_version, returns JIT provider's version string and
+ * reports if it's not available.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ const char *version;
+
+ if (!provider_init())
+ {
+ *available = false;
+ return "";
+ }
+
+ version = provider.get_version();
+
+ if (version == NULL)
+ {
+ *available = false;
+ return "";
+ }
+
+ *available = true;
+ return version;
+}
Perhaps more elegant would be:
if (provider_init())
{
version = provider.get_version();
if (version)
{
*available = true;
return version;
}
}
*available = false;
return "";
Other than that, it looks fine.
Yours,
Laurenz Albe
On Wed, Nov 26, 2025 at 02:28:06PM +0100, Laurenz Albe wrote:
You didn't address any of my suggestions concerning the documentation,
except that you moved the entry in "System Views" to the correct place.
I did address the build failure and the order of entries, but looks like
I've overlooked the spelling. Was there anything else?
On Wed, 2025-11-26 at 14:53 +0100, Dmitry Dolgov wrote:
On Wed, Nov 26, 2025 at 02:28:06PM +0100, Laurenz Albe wrote:
You didn't address any of my suggestions concerning the documentation,
except that you moved the entry in "System Views" to the correct place.I did address the build failure and the order of entries, but looks like
I've overlooked the spelling. Was there anything else?
In the documentation of the "type" column, I suggest using
Compile Time or Run Time
(capitalization like in the view results), or even better
<quote>Compile Time</quote> or <quote>Run Time</quote>
Yours,
Laurenz Albe
On Wed, Nov 26, 2025 at 04:39:50PM +0100, Laurenz Albe wrote:
In the documentation of the "type" column, I suggest usingCompile Time or Run Time
(capitalization like in the view results), or even better
<quote>Compile Time</quote> or <quote>Run Time</quote>
Yeah, I've stored them both under "spelling" in my head :) Now
everything should be there.
But that belongs into the third patch.
Good catch, moved this chunk.
Perhaps more elegant would be:
I was considering this originally, but my dislike of nested conditions
has prevailed. At the same time I don't mind returning to this version.
I've also realized that MinGW and Visual Studio failures were in fact
due to the uchar.h include not being guarded by USE_ICU.
Attachments:
v6-0001-Add-infrastructure-for-pg_system_versions-view.patchtext/plain; charset=us-asciiDownload
From 5a22e997453c3c1b1c5f72fbdc6821b8ef4729b9 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sun, 16 Nov 2025 16:01:21 +0100
Subject: [PATCH v6 1/3] Add infrastructure for pg_system_versions view
Introduce a unified way of reporting versions (PostgreSQL itself, the
compiler, the host system, compile and runtime dependencies, etc.) via a
new system view pg_system_versions. This is going to be useful for
troubleshooting and should enhance bug reports, replacing manual
bug-prone collecting of the same information.
The view is backed by a hash table, that contains callbacks returning
version string for a particular component. The idea is to allow some
flexibility in reporting, making components responsible for how and when
the information is exposed.
---
doc/src/sgml/system-views.sgml | 65 +++++++++++++++
src/backend/catalog/system_views.sql | 8 ++
src/backend/utils/misc/Makefile | 3 +-
src/backend/utils/misc/meson.build | 1 +
src/backend/utils/misc/system_version.c | 105 ++++++++++++++++++++++++
src/include/catalog/pg_proc.dat | 6 ++
src/include/utils/system_version.h | 38 +++++++++
src/test/regress/expected/rules.out | 8 ++
src/tools/pgindent/typedefs.list | 2 +
9 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 src/backend/utils/misc/system_version.c
create mode 100644 src/include/utils/system_version.h
diff --git a/doc/src/sgml/system-views.sgml b/doc/src/sgml/system-views.sgml
index 7971498fe75..34f88c0b0d9 100644
--- a/doc/src/sgml/system-views.sgml
+++ b/doc/src/sgml/system-views.sgml
@@ -211,6 +211,11 @@
<entry>extended planner statistics for expressions</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-system-versions"><structname>pg_system_versions</structname></link></entry>
+ <entry>system versions</entry>
+ </row>
+
<row>
<entry><link linkend="view-pg-tables"><structname>pg_tables</structname></link></entry>
<entry>tables</entry>
@@ -4986,6 +4991,66 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
</sect1>
+ <sect1 id="view-pg-system-versions">
+ <title><structname>pg_system_versions</structname></title>
+
+ <indexterm zone="view-pg-system-versions">
+ <primary>pg_system_versions</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_system_versions</structname> provides description
+ about the versions of various system components, e.g. PostgreSQL itself,
+ the compiler used to build it, dependencies.
+ </para>
+
+ <table>
+ <title><structname>pg_system_versions</structname> Columns</title>
+ <tgroup cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>name</structfield> <type>text</type>
+ </para>
+ <para>
+ Component name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>version</structfield> <type>text</type>
+ </para>
+ <para>
+ Component version
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>type</structfield> <type>text</type>
+ </para>
+ <para>
+ Component type (Compile Time or Run Time)
+ </para></entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="view-pg-tables">
<title><structname>pg_tables</structname></title>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 059e8778ca7..a00777e4f8b 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1438,3 +1438,11 @@ REVOKE ALL ON pg_aios FROM PUBLIC;
GRANT SELECT ON pg_aios TO pg_read_all_stats;
REVOKE EXECUTE ON FUNCTION pg_get_aios() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION pg_get_aios() TO pg_read_all_stats;
+
+CREATE VIEW pg_system_versions AS
+ SELECT
+ name, version,
+ CASE type WHEN 0 THEN 'Compile Time'
+ WHEN 1 THEN 'Run Time'
+ END AS "type"
+ FROM pg_get_system_versions();
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index f142d17178b..0ac5b3c79cd 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -32,7 +32,8 @@ OBJS = \
stack_depth.o \
superuser.o \
timeout.o \
- tzparser.o
+ tzparser.o \
+ system_version.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build
index 9e389a00d05..5268eaa94c7 100644
--- a/src/backend/utils/misc/meson.build
+++ b/src/backend/utils/misc/meson.build
@@ -16,6 +16,7 @@ backend_sources += files(
'sampling.c',
'stack_depth.c',
'superuser.c',
+ 'system_version.c',
'timeout.c',
'tzparser.c',
)
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
new file mode 100644
index 00000000000..f073f62ca75
--- /dev/null
+++ b/src/backend/utils/misc/system_version.c
@@ -0,0 +1,105 @@
+/*------------------------------------------------------------------------
+ *
+ * system_version.c
+ * Functions for reporting version of system components.
+ *
+ * A system component is defined very broadly here, it might be the PostgreSQL
+ * core itself, the compiler, the host system, any dependency that is used at
+ * compile time or run time.
+ *
+ * Version reporting is implemented via a hash table containing the component's
+ * name as a key and the callback to fetch the version string. Every component
+ * can register such a callback during initialization and is responsible for
+ * exposing its own information. The idea is that storing a callback instead of
+ * a version string directly allows for more flexibility about how and when the
+ * information could be reported.
+ *
+ * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/system_version.c
+ *
+ *------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/system_version.h"
+
+static HTAB *versions = NULL;
+
+void
+add_system_version(const char *name, SystemVersionCB cb, VersionType type)
+{
+ SystemVersion *hentry;
+ const char *key;
+ bool found;
+
+ if (!versions)
+ {
+ HASHCTL ctl;
+
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(SystemVersion);
+
+ versions = hash_create("System versions table",
+ MAX_SYSTEM_VERSIONS,
+ &ctl,
+ HASH_ELEM | HASH_STRINGS);
+ }
+
+ key = pstrdup(name);
+ Assert(strlen(key) < NAMEDATALEN);
+
+ hentry = (SystemVersion *) hash_search(versions, key,
+ HASH_ENTER, &found);
+
+ /* Duplicated entries are not expected */
+ Assert(!found);
+
+ hentry->callback = cb;
+ hentry->type = type;
+}
+
+/*
+ * pg_get_system_versions
+ *
+ * List information about system versions.
+ */
+Datum
+pg_get_system_versions(PG_FUNCTION_ARGS)
+{
+#define PG_GET_SYS_VERSIONS_COLS 3
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ HASH_SEQ_STATUS status;
+ SystemVersion *hentry;
+
+ /* Build tuplestore to hold the result rows */
+ InitMaterializedSRF(fcinfo, 0);
+
+ if (!versions)
+ return (Datum) 0;
+
+ hash_seq_init(&status, versions);
+ while ((hentry = (SystemVersion *) hash_seq_search(&status)) != NULL)
+ {
+ Datum values[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool nulls[PG_GET_SYS_VERSIONS_COLS] = {0};
+ bool available = false;
+ const char *version = hentry->callback(&available);
+
+ if (!available)
+ continue;
+
+ values[0] = CStringGetTextDatum(hentry->name);
+ values[1] = CStringGetTextDatum(version);
+ values[2] = Int32GetDatum(hentry->type);
+
+ tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
+ }
+
+ return (Datum) 0;
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 5cf9e12fcb9..858e2f9983b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12604,4 +12604,10 @@
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' },
+{ oid => '9432', descr => 'describe system verions',
+ proname => 'pg_get_system_versions', procost => '10', prorows => '10',
+ proretset => 't', provolatile => 'v', prorettype => 'record',
+ proargtypes => '', proallargtypes => '{text,text,int8}',
+ proargmodes => '{o,o,o}', proargnames => '{name,version,type}',
+ prosrc => 'pg_get_system_versions' },
]
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
new file mode 100644
index 00000000000..18cb673d4ca
--- /dev/null
+++ b/src/include/utils/system_version.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ * system_version.h
+ * Definitions related to system versions reporting
+ *
+ * Copyright (c) 2001-2024, PostgreSQL Global Development Group
+ *
+ * src/include/utils/system_version.h
+ * ----------
+ */
+
+#ifndef SYSTEM_VERSION_H
+#define SYSTEM_VERSION_H
+
+#define MAX_SYSTEM_VERSIONS 100
+
+typedef enum VersionType
+{
+ CompileTime,
+ RunTime,
+} VersionType;
+
+/*
+ * Callback to return version string of a system component.
+ * The version might be not available, what is indicated via the argument.
+ */
+typedef const char *(*SystemVersionCB) (bool *available);
+
+typedef struct SystemVersion
+{
+ char name[NAMEDATALEN]; /* Unique component name, used as a key
+ * for versions HTAB */
+ VersionType type;
+ SystemVersionCB callback; /* Callback to fetch the version string */
+} SystemVersion;
+
+void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+
+#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 7c52181cbcb..7db23198ff3 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -2683,6 +2683,14 @@ pg_stats_ext_exprs| SELECT cn.nspname AS schemaname,
JOIN LATERAL ( SELECT unnest(pg_get_statisticsobjdef_expressions(s.oid)) AS expr,
unnest(sd.stxdexpr) AS a) stat ON ((stat.expr IS NOT NULL)))
WHERE (pg_has_role(c.relowner, 'USAGE'::text) AND ((c.relrowsecurity = false) OR (NOT row_security_active(c.oid))));
+pg_system_versions| SELECT name,
+ version,
+ CASE type
+ WHEN 0 THEN 'Compile Time'::text
+ WHEN 1 THEN 'Run Time'::text
+ ELSE NULL::text
+ END AS type
+ FROM pg_get_system_versions() pg_get_system_versions(name, version, type);
pg_tables| SELECT n.nspname AS schemaname,
c.relname AS tablename,
pg_get_userbyid(c.relowner) AS tableowner,
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 23bce72ae64..5ebb83d9899 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2934,6 +2934,7 @@ SysloggerStartupData
SystemRowsSamplerData
SystemSamplerData
SystemTimeSamplerData
+SystemVersion
TAPtype
TAR_MEMBER
TBMIterateResult
@@ -4215,6 +4216,7 @@ varattrib_1b_e
varattrib_4b
vbits
verifier_context
+VersionType
walrcv_alter_slot_fn
walrcv_check_conninfo_fn
walrcv_connect_fn
--
2.49.0
v6-0002-Add-core-versions-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 086b5a5273a7abc7d21c26a07b160270f7628e1f Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Wed, 26 Nov 2025 20:05:20 +0100
Subject: [PATCH v6 2/3] Add core versions to pg_system_versions
Populate pg_system_versions with a set of core versions: host system
architecture, ICU version, glibc version, PostgreSQL itself and compiler
which was used to build everything. Register the core versions at the
backend startup.
select * from pg_system_versions;
name | version | type
----------+--------------+--------------
Arch | x86_64-linux | Compile Time
ICU | 15.1 | Run Time
Core | 18devel | Compile Time
Compiler | gcc-14.0.1 | Compile Time
Glibc | 2.40 | Run Time
---
configure | 12 +++++
configure.ac | 4 ++
meson.build | 4 ++
src/backend/tcop/postgres.c | 12 +++++
src/backend/utils/misc/system_version.c | 72 +++++++++++++++++++++++++
src/include/pg_config.h.in | 4 ++
src/include/utils/system_version.h | 1 +
src/test/regress/expected/sysviews.out | 10 ++++
src/test/regress/sql/sysviews.sql | 6 +++
9 files changed, 125 insertions(+)
diff --git a/configure b/configure
index 3a0ed11fa8e..f7d5f695679 100755
--- a/configure
+++ b/configure
@@ -19293,6 +19293,18 @@ else
fi
+cat >>confdefs.h <<_ACEOF
+#define PG_CC_STR "$cc_string"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define PG_ARCH_STR "$host"
+_ACEOF
+
+
+
cat >>confdefs.h <<_ACEOF
#define PG_VERSION_STR "PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
_ACEOF
diff --git a/configure.ac b/configure.ac
index c2413720a18..9f7f7f75535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2448,6 +2448,10 @@ else
cc_string=$CC
fi
+AC_DEFINE_UNQUOTED(PG_CC_STR, ["$cc_string"], [C compiler version])
+
+AC_DEFINE_UNQUOTED(PG_ARCH_STR, ["$host"], [Platform])
+
AC_DEFINE_UNQUOTED(PG_VERSION_STR,
["PostgreSQL $PG_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
[A string containing the version number, platform, and C compiler])
diff --git a/meson.build b/meson.build
index c1e17aa3040..2265eaafb48 100644
--- a/meson.build
+++ b/meson.build
@@ -2972,6 +2972,10 @@ cdata.set('USE_@0@_SEMAPHORES'.format(sema_kind.to_upper()), 1)
cdata.set('MEMSET_LOOP_LIMIT', memset_loop_limit)
cdata.set_quoted('DLSUFFIX', dlsuffix)
+cdata.set_quoted('PG_CC_STR', '@0@-@1@'.format(cc.get_id(), cc.version()))
+
+cdata.set_quoted('PG_ARCH_STR', '@0@-@1@'.format(
+ host_machine.cpu_family(), host_system))
# built later than the rest of the version metadata, we need SIZEOF_VOID_P
cdata.set_quoted('PG_VERSION_STR',
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7dd75a490aa..3a8ff419425 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -81,6 +81,7 @@
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
+#include "utils/system_version.h"
/* ----------------
* global variables
@@ -186,6 +187,7 @@ static void drop_unnamed_stmt(void);
static void log_disconnections(int code, Datum arg);
static void enable_statement_timeout(void);
static void disable_statement_timeout(void);
+static void register_system_versions(void);
/* ----------------------------------------------------------------
@@ -4318,6 +4320,9 @@ PostgresMain(const char *dbname, const char *username)
*/
BeginReportingGUCOptions();
+ /* Prepare information for reporting versions and libraries. */
+ register_system_versions();
+
/*
* Also set up handler to log session end; we have to wait till now to be
* sure Log_disconnections has its final value.
@@ -5237,3 +5242,10 @@ disable_statement_timeout(void)
if (get_timeout_active(STATEMENT_TIMEOUT))
disable_timeout(STATEMENT_TIMEOUT, false);
}
+
+static void
+register_system_versions()
+{
+ /* Set up reporting of core versions. */
+ register_core_versions();
+}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index f073f62ca75..a6061a7c6b6 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -25,6 +25,14 @@
*/
#include "postgres.h"
+#ifdef USE_ICU
+#include <unicode/uchar.h>
+#endif
+
+#ifdef __GLIBC__
+#include <gnu/libc-version.h>
+#endif
+
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/system_version.h"
@@ -64,6 +72,70 @@ add_system_version(const char *name, SystemVersionCB cb, VersionType type)
hentry->type = type;
}
+static const char *
+core_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_VERSION);
+}
+
+static const char *
+core_get_arch(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_ARCH_STR);
+}
+
+static const char *
+core_get_compiler(bool *available)
+{
+ *available = true;
+ return (const char *) psprintf("%s", PG_CC_STR);
+}
+
+#ifdef USE_ICU
+static const char *
+icu_get_version(bool *available)
+{
+ UVersionInfo UCDVersion;
+ char *version = palloc0(U_MAX_VERSION_STRING_LENGTH);
+
+ *available = true;
+ u_getUnicodeVersion(UCDVersion);
+ u_versionToString(UCDVersion, version);
+ return (const char *) version;
+}
+#endif
+
+#ifdef __GLIBC__
+static const char *
+glibc_get_version(bool *available)
+{
+ *available = true;
+ return (const char *) gnu_get_libc_version();
+}
+#endif
+
+/*
+ * Register versions that describe core components and do not correspond to any
+ * individual component.
+ */
+void
+register_core_versions()
+{
+ add_system_version("Core", core_get_version, CompileTime);
+ add_system_version("Arch", core_get_arch, CompileTime);
+ add_system_version("Compiler", core_get_compiler, CompileTime);
+
+#ifdef USE_ICU
+ add_system_version("ICU", icu_get_version, RunTime);
+#endif
+
+#ifdef __GLIBC__
+ add_system_version("Glibc", glibc_get_version, RunTime);
+#endif
+}
+
/*
* pg_get_system_versions
*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..dacf496ce71 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -621,6 +621,10 @@
/* PostgreSQL version as a number */
#undef PG_VERSION_NUM
+#undef PG_CC_STR
+
+#undef PG_ARCH_STR
+
/* A string containing the version number, platform, and C compiler */
#undef PG_VERSION_STR
diff --git a/src/include/utils/system_version.h b/src/include/utils/system_version.h
index 18cb673d4ca..49685b6809f 100644
--- a/src/include/utils/system_version.h
+++ b/src/include/utils/system_version.h
@@ -34,5 +34,6 @@ typedef struct SystemVersion
} SystemVersion;
void add_system_version(const char *name, SystemVersionCB cb, VersionType type);
+extern void register_core_versions(void);
#endif /* SYSTEM_VERSION_H */
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 3b37fafa65b..90c2fdeba2d 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -233,3 +233,13 @@ select * from pg_timezone_abbrevs where abbrev = 'LMT';
LMT | @ 7 hours 52 mins 58 secs ago | f
(1 row)
+-- Check that the pg_system_versions contains something. Three core versions
+-- should be present: architecture, core and compiler and glibc. More records
+-- may be present depending on the build configuration (e.g. with ICU, GlibC or
+-- JIT support).
+select exists (table pg_system_versions);
+ exists
+--------
+ t
+(1 row)
+
diff --git a/src/test/regress/sql/sysviews.sql b/src/test/regress/sql/sysviews.sql
index 66179f026b3..c6328ec3d57 100644
--- a/src/test/regress/sql/sysviews.sql
+++ b/src/test/regress/sql/sysviews.sql
@@ -101,3 +101,9 @@ select count(distinct utc_offset) >= 24 as ok from pg_timezone_abbrevs;
-- One specific case we can check without much fear of breakage
-- is the historical local-mean-time value used for America/Los_Angeles.
select * from pg_timezone_abbrevs where abbrev = 'LMT';
+
+-- Check that the pg_system_versions contains something. Three core versions
+-- should be present: architecture, core and compiler and glibc. More records
+-- may be present depending on the build configuration (e.g. with ICU, GlibC or
+-- JIT support).
+select exists (table pg_system_versions);
--
2.49.0
v6-0003-Add-JIT-provider-version-to-pg_system_versions.patchtext/plain; charset=us-asciiDownload
From 6c451ae14646f28733d9ff52a660c19de9a817d7 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <9erthalion6@gmail.com>
Date: Sat, 5 Oct 2024 18:31:36 +0200
Subject: [PATCH v6 3/3] Add JIT provider version to pg_system_versions
Populate pg_system_versions with the JIT provider version. To actually
fetch the version, extend the JIT provider callbacks with the
get_version method. For LLVM provider llvm_version will be used, which
utilizes C-API LLVMGetVersion, available since LLVM 16.
The JIT provider will be initialized, when a first expression will be
compiled. For reporting purposes it's too late, thus register the
version at the backend startup, right after the core versions.
---
src/backend/jit/jit.c | 24 ++++++++++++++++++++++++
src/backend/jit/llvm/llvmjit.c | 17 +++++++++++++++++
src/backend/tcop/postgres.c | 7 +++++++
src/backend/utils/misc/system_version.c | 7 +++++++
src/include/jit/jit.h | 9 +++++++++
src/include/jit/llvmjit.h | 2 ++
6 files changed, 66 insertions(+)
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index d2ccef9de85..9ffefde2a8d 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -188,3 +188,27 @@ InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
}
+
+/*
+ * Callback for add_system_version, returns JIT provider's version string and
+ * reports if it's not available.
+ */
+const char *
+jit_get_version(bool *available)
+{
+ const char *version;
+
+ if (provider_init())
+ {
+ version = provider.get_version();
+
+ if (version)
+ {
+ *available = true;
+ return version;
+ }
+ }
+
+ *available = false;
+ return "";
+}
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index e978b996bae..b2f445963ab 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -154,6 +154,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
cb->reset_after_error = llvm_reset_after_error;
cb->release_context = llvm_release_context;
cb->compile_expr = llvm_compile_expr;
+ cb->get_version = llvm_version;
}
@@ -1280,3 +1281,19 @@ ResOwnerReleaseJitContext(Datum res)
context->resowner = NULL;
jit_release_context(&context->base);
}
+
+const char *
+llvm_version()
+{
+#if LLVM_VERSION_MAJOR > 15
+ unsigned int major,
+ minor,
+ patch;
+
+ LLVMGetVersion(&major, &minor, &patch);
+
+ return (const char *) psprintf("%d.%d.%d", major, minor, patch);
+#else
+ return NULL;
+#endif
+}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3a8ff419425..a23a858f2c9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -5248,4 +5248,11 @@ register_system_versions()
{
/* Set up reporting of core versions. */
register_core_versions();
+
+ /*
+ * Set up reporting for JIT provider version. JIT provider initialization
+ * happens when the first expression is getting compiled, which is too
+ * late. Thus register the callback here instead.
+ */
+ jit_register_version();
}
diff --git a/src/backend/utils/misc/system_version.c b/src/backend/utils/misc/system_version.c
index a6061a7c6b6..7721af45362 100644
--- a/src/backend/utils/misc/system_version.c
+++ b/src/backend/utils/misc/system_version.c
@@ -34,6 +34,7 @@
#endif
#include "funcapi.h"
+#include "jit/jit.h"
#include "utils/builtins.h"
#include "utils/system_version.h"
@@ -116,6 +117,12 @@ glibc_get_version(bool *available)
}
#endif
+void
+jit_register_version(void)
+{
+ add_system_version("LLVM", jit_get_version, RunTime);
+}
+
/*
* Register versions that describe core components and do not correspond to any
* individual component.
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index 33cb36c5d2e..5342a282fb4 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -70,12 +70,14 @@ typedef void (*JitProviderResetAfterErrorCB) (void);
typedef void (*JitProviderReleaseContextCB) (JitContext *context);
struct ExprState;
typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
+typedef const char *(*JitProviderVersion) (void);
struct JitProviderCallbacks
{
JitProviderResetAfterErrorCB reset_after_error;
JitProviderReleaseContextCB release_context;
JitProviderCompileExprCB compile_expr;
+ JitProviderVersion get_version;
};
@@ -102,5 +104,12 @@ extern void jit_release_context(JitContext *context);
extern bool jit_compile_expr(struct ExprState *state);
extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
+/*
+ * Callback for add_system_version, get the provider's version string. The flag
+ * indicating availability is passed as an argument.
+ */
+extern const char *jit_get_version(bool *available);
+
+extern void jit_register_version(void);
#endif /* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index b3c75022f55..1c903a4dc52 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -145,6 +145,8 @@ extern LLVMTypeRef LLVMGetFunctionType(LLVMValueRef r);
extern LLVMOrcObjectLayerRef LLVMOrcCreateRTDyldObjectLinkingLayerWithSafeSectionMemoryManager(LLVMOrcExecutionSessionRef ES);
#endif
+extern const char* llvm_version(void);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
--
2.49.0
Thanks, the patch looks good now.
There is one tiny thing that I noticed only now, sorry:
--- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -32,7 +32,8 @@ OBJS = \ stack_depth.o \ superuser.o \ timeout.o \ - tzparser.o + tzparser.o \ + system_version.o
That looks like an alphabetically sorted list too, so you should preserve
that ordering.
Anyway, I'll set this patch "ready for committer".
I am not sure if this reduced form (no report on zstd, lz4, openssl,
zlib etc.) is good enough for release, but I'll leave that decision to a committer.
Yours,
Laurenz Albe