PATCH: Report libpq version and configuration
The attached patches propose new interfaces for exposing more configuration
and versioning information from libpq at runtime. They are to be used by
applications to obtain finer grained information about libpq's
configuration (SSL, GSSAPI, etc), to identify libpq binaries, and for
applications that use libpq to report diagnostic information
Patch 0001 adds PQlibInfo(), which returns an array of key/value
description items reporting on configuration like the full version string,
SSL support, gssapi support, thread safety, default port and default unix
socket path. This is for application use and application diagnostics. It
also adds PQlibInfoPrint() which dumps PQlibInfo() keys/values to stdout.
See the commit message in patch 0001 for details.
Patch 0002 exposes LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS symbols in the dynamic symbol table. These can be
accessed by a debugger even when the library cannot be loaded or executed,
and unlike macros are available even in a stripped executable. So they can
be used to identify a libpq binary found in the wild. Their storage is
shared with PQlibInfo()'s static data, so they only cost three symbol table
entries.
$ cp ./build/src/interfaces/libpq/libpq.so libpq.so.stripped
$ strip libpq.so.stripped
$ gdb -batch -ex 'p (int)LIBPQ_VERSION_NUM' -ex 'p (const char
*)LIBPQ_VERSION_STR' -ex 'p (const char *)LIBPQ_CONFIGURE_ARGS'
./libpq.so.stripped
$1 = 140000
$2 = 0x285f0 "PostgreSQL 14devel on x86_64-pc-linux-gnu, ...."
$3 = 0x28660 " '--cache-file=config.cache-'
'--prefix=/home/craig/pg/master' '--enable-debug' '--enable-cassert'
'--enable-tap-tests' '--enable-dtrace' 'CC=/usr/lib64/ccache/gcc'
'CFLAGS=-Og -ggdb3' ..."
Patch 0003 allows libpq.so to be executed directly from the command line to
print its version, configure arguments etc exactly as PQlibInfoPrint()
would output them. This is only enabled on x64 linux for now but can be
extended to other targets quite simply.
$ ./build/src/interfaces/libpq/libpq.so
VERSION_NUM: 140000
VERSION: PostgreSQL 14devel on x86_64-pc-linux-gnu, compiled by gcc (GCC)
10.2.1 20200723 (Red Hat 10.2.1-1), 64-bit
CONFIGURE_ARGS: '--cache-file=config.cache-'
'--prefix=/home/craig/pg/master' '--enable-debug' '--enable-cassert'
'--enable-tap-tests' '--enable-dtrace' 'CC=/usr/lib64/ccache/gcc'
'CFLAGS=-Og -ggdb3' 'CPPFLAGS=' 'CPP=/usr/lib64/ccache/gcc -E'
USE_SSL: 0
ENABLE_GSS: 0
ENABLE_THREAD_SAFETY: 1
HAVE_UNIX_SOCKETS: 1
DEFAULT_PGSOCKET_DIR: /tmp
DEF_PGPORT: 5432
Attachments:
v1-0002-Add-libpq-version-information-to-dynamic-symbol-t.patchtext/x-patch; charset=US-ASCII; name=v1-0002-Add-libpq-version-information-to-dynamic-symbol-t.patchDownload
From a4741725a9596b25a9b7308b6a47e85b889d4557 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig.ringer@2ndquadrant.com>
Date: Mon, 26 Oct 2020 20:43:24 +0800
Subject: [PATCH v1 2/3] Add libpq version information to dynamic symbol table
Add three new data symbols LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS to libpq's symbol table. These make it much easier to
identify a given libpq without debuginfo, even if it cannot be executed (e.g.
due to dependency problems or mismatched platforms).
$ strip libpq.so
$ gdb -batch -ex 'p (int)LIBPQ_VERSION_NUM' \
-ex 'p (const char *)LIBPQ_VERSION_STR' \
-ex 'p (const char *)LIBPQ_CONFIGURE_ARGS' \
./libpq.so.stripped
$1 = 140000
$2 = 0x285f0 "PostgreSQL 14devel on x86_64 ...."
$3 = 0x28660 " '--enable-debug' '--enable-cassert' ...."
---
src/interfaces/libpq/exports.txt | 3 +++
src/interfaces/libpq/libpq-version.c | 32 +++++++++++++++++++++++++---
2 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 1d8600f0d8..64fb62c802 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -181,3 +181,6 @@ PQgetSSLKeyPassHook_OpenSSL 178
PQdefaultSSLKeyPassHook_OpenSSL 179
PQlibInfo 180
PQlibInfoPrint 181
+LIBPQ_VERSION_STR 182
+LIBPQ_VERSION_NUM 183
+LIBPQ_CONFIGURE_ARGS 184
diff --git a/src/interfaces/libpq/libpq-version.c b/src/interfaces/libpq/libpq-version.c
index 51a5c608c8..bab0263848 100644
--- a/src/interfaces/libpq/libpq-version.c
+++ b/src/interfaces/libpq/libpq-version.c
@@ -47,21 +47,47 @@
#define libinfo_boolstr(x) (x) == 1 ? gettext_noop("1") : gettext_noop("0")
+/*
+ * Store postgres version information in const symbols in the read-only data
+ * section of the libpq binary so they are exposed in the export symbol table,
+ * not just as macros.
+ *
+ * This way the exact version of a given libpq.so / libpq.dll can be determined
+ * even if it was built without macros in debuginfo or if is stripped, and
+ * without necessarily being able to execute the binary (e.g. due to linker
+ * issues).
+ *
+ * We need to store these for PGlibInfoEntries anyway, this just makes them
+ * individually addressable for the use of trace/diagnostic tools and
+ * debuggers.
+ *
+ * The linker will put these in read-only data segments so they won't use extra
+ * dynamic memory at runtime.
+ *
+ * You should prefer to use the normal methods of getting version info in your
+ * applications - PQlibVersion(), PG_VERSION_NUM, the server_version_num GUC,
+ * etc.
+ */
+
+const char * const LIBPQ_VERSION_STR = PG_VERSION_STR;
+const int LIBPQ_VERSION_NUM = PG_VERSION_NUM;
+const char * const LIBPQ_CONFIGURE_ARGS = CONFIGURE_ARGS;
+
static const PGlibInfoEntry PGlibInfoEntries[] = {
{
PGRES_LIBINFO_VERSION_NUM,
gettext_noop("VERSION_NUM"),
- CppAsString2(PG_VERSION_NUM), 1, PG_VERSION_NUM
+ CppAsString2(PG_VERSION_NUM), 1, LIBPQ_VERSION_NUM
},
{
PGRES_LIBINFO_VERSION_STR,
gettext_noop("VERSION"),
- PG_VERSION_STR, 0, 0
+ LIBPQ_VERSION_STR, 0, 0
},
{
PGRES_LIBINFO_CONFIGURE_ARGS,
gettext_noop("CONFIGURE_ARGS"),
- CONFIGURE_ARGS, 0, 0
+ LIBPQ_CONFIGURE_ARGS, 0, 0
},
{
PGRES_LIBINFO_USE_SSL,
--
2.26.2
v1-0003-On-x64-linux-run-PQlibInfoPrint-when-libpq.so-is-.patchtext/x-patch; charset=US-ASCII; name=v1-0003-On-x64-linux-run-PQlibInfoPrint-when-libpq.so-is-.patchDownload
From d0eb44fc066d9125dbb097d76975be8278722586 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig.ringer@2ndquadrant.com>
Date: Mon, 26 Oct 2020 16:39:50 +0800
Subject: [PATCH v1 3/3] On x64 linux, run PQlibInfoPrint() when libpq.so is
executed
Allow libpq.so to be directly executed. When run, it emits the same version and
configuration information as PQlibInfoPrint().
This approach should work on any or all ELF platforms with dynamic linker
support, but the toolchain support for producing a -shared library that
has an interpreter set in its headers is presently a bit limited.
---
doc/src/sgml/libpq.sgml | 7 +++++++
src/interfaces/libpq/Makefile | 5 +++++
src/interfaces/libpq/libpq-version.c | 28 ++++++++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index f0e2255d18..77e0e66ebc 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -6436,6 +6436,13 @@ void PQlibInfoPrint(void);
returned by <xref linkend="libpq-PQlibInfo"/> to the application's
default stdio output stream (stdout).
</para>
+ <para>
+ On Linux the same output may be obtained by executing <filename>libpq.so</filename>
+ directly, e.g.
+ <programlisting>
+ $ /usr/pgsql-11/lib/libpq.so
+ </programlisting>
+ </para>
<note>
<para>
This function appeared in <productname>PostgreSQL</productname> version 14.
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 8a9a6f4c09..330e00bfc9 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -90,6 +90,11 @@ SHLIB_PREREQS = submake-libpgport
SHLIB_EXPORTS = exports.txt
+# Support ./libpq.so execution of the built library; see libpq-version.c
+ifeq ($(host_tuple),x86_64-pc-linux-gnu)
+SHLIB_LINK += -Wl,--entry=libpq_exec_main
+endif
+
PKG_CONFIG_REQUIRES_PRIVATE = libssl libcrypto
all: all-lib
diff --git a/src/interfaces/libpq/libpq-version.c b/src/interfaces/libpq/libpq-version.c
index bab0263848..a24496360d 100644
--- a/src/interfaces/libpq/libpq-version.c
+++ b/src/interfaces/libpq/libpq-version.c
@@ -148,3 +148,31 @@ PQlibInfoPrint(void)
ientry ++;
}
}
+
+/*
+ * If gcc and ld or clang and ld.lld would offer a --dynamic-linker=default
+ * or similar to force the .interp section to be emitted when building with
+ * -shared, we wouldn't need this. As it is, limit support for running
+ * libpq as an executable to x64 linux.
+ */
+#if defined(__GNUC__) && defined(__linux__) && defined(__x86_64__)
+const char interp_section[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
+
+/*
+ * Report library version and configuration by dumping PGlibInfoEntries to
+ * stdout when libpq is invoked as ./libpq.so. Do not call this when loading
+ * libpq as a shared library. Use PQlibInfo() instead.
+ *
+ * This won't work on Windows. You can abuse it with rundll32 if you must, but
+ * that's deprecated and should not be relied upon.
+ */
+extern void
+libpq_exec_main(void);
+
+void
+libpq_exec_main(void)
+{
+ PQlibInfoPrint();
+ exit(0);
+}
+#endif
--
2.26.2
v1-0001-Add-PQlibInfo-to-get-runtime-configuration-of-cur.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Add-PQlibInfo-to-get-runtime-configuration-of-cur.patchDownload
From 7be4425d5f9c89f3fdd7946370efe3a20ec0d2a6 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig.ringer@2ndquadrant.com>
Date: Mon, 26 Oct 2020 12:53:16 +0800
Subject: [PATCH v1 1/3] Add PQlibInfo() to get runtime configuration of
current libpq
The new PQlibInfo() function allows applications linked to libpq to determine
whether the current libpq is built with SSL/TLS, GSSAPI, thread safety etc at
runtime. This lets applications check that the libpq the dynamic linker picked
actually matches the libpq they were compiled against. It also provides a way
for applications to include more useful libpq diagnostic and descriptive
information in their own diagnostic output commands.
Applications can also use PQlibInfo() to obtain finer-grained intput into
runtime decisions about whether to use certain libpq features. It is already
possible for applications to use lazy dynamic linking (RTLD_LAZY) or runtime
dynamic linking (dlopen) and use the return value from PQlibVersion() to decide
whether to attempt to use certain API functions. The addition of PQlibInfo()
extends this capability to potentially allow applications to access
compile-time optional library features. The application can test for the
availablity of the optional libpq APIs, so there's no requirement for libpq to
be able to expose sensible stubs when the functionality isn't configured or
available.
For covenient diagnostic use the new function PQlibInfoPrint() prints all
keys and values from PQlibInfo() to stdout.
---
doc/src/sgml/libpq.sgml | 195 ++++++++++++++++++++++++++-
src/interfaces/libpq/Makefile | 1 +
src/interfaces/libpq/exports.txt | 2 +
src/interfaces/libpq/libpq-fe.h | 6 +-
src/interfaces/libpq/libpq-version.c | 124 +++++++++++++++++
src/interfaces/libpq/libpq-version.h | 46 +++++++
6 files changed, 372 insertions(+), 2 deletions(-)
create mode 100644 src/interfaces/libpq/libpq-version.c
create mode 100644 src/interfaces/libpq/libpq-version.h
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index de60281fcb..f0e2255d18 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -6251,6 +6251,199 @@ int PQlibVersion(void);
</listitem>
</varlistentry>
+ <varlistentry id="libpq-PQlibInfo">
+ <term><function>PQlibInfo</function><indexterm
+ ><primary>PQlibInfo</primary><seealso>PQlibVersion</seealso></indexterm></term>
+
+ <listitem>
+ <para>
+ Return an array of <structname>PGlibInfoEntry</structname> describing the
+ version and configuration of the currently loaded <filename>libpq</filename>.
+<synopsis>
+/* libpq-version.h */
+const PGlibInfoEntry * PQlibInfo(void);
+</synopsis>
+ </para>
+
+ <para>
+ The result of <function>PQlibInfo()</function> provides details about
+ the <filename>libpq</filename> that the application is actually linked
+ to at runtime. It exposes finer-grained information than is available
+ from <function>PQlibVersion()</function>.
+ </para>
+
+ <para>
+ Applications must include <filename>libpq-version.h</filename> to use
+ <function>PQlibInfo()</function>.
+ </para>
+
+ <para>
+ The return value is a pointer to a statically allocated array of
+ <structname>PGlibInfoEntry</structname>:
+ <variablelist id="libpq-struct-PGlibInfoEntry" xreflabel="struct PGlibInfoEntry">
+ <title><structname>PGlibInfoEntry</structname></title>
+ <varlistentry>
+ <term><type>enum PGlibInfoEntryKey</type> <structfield>key</structfield></term>
+ <listitem>
+ <para>
+ <xref linkend="libpq-enum-PGlibInfoEntryKey"/> identifying the entry, or
+ <literal>PGRES_LIBINFO_END_OF_LIST</literal> (0) for the end-of-array
+ sentinel value. The application should ignore entries with unrecognised
+ values for this field.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><type>const char *</type> <structfield>key_name</structfield></term>
+ <listitem>
+ <para>String representation of <structfield>key</structfield></para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><type>const char *</type> <structfield>value_str</structfield></term>
+ <listitem>
+ <para>Printable string describing key's value. Should never be 0.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><type>bool</type> <structfield>has_intvalue</structfield></term>
+ <listitem>
+ <para>1 if the <structfield>value_int</structfield> is valid, otherwise 0.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><type>int</type> <structfield>value_str</structfield></term>
+ <listitem>
+ <para>Integer describing key's value, if <structfield>value_int</structfield> is true.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ The array is terminated by an entry with <structfield>key</structfield> <literal>==</literal>
+ <literal>PGRES_LIBINFO_END_OF_LIST</literal> (0). The caller
+ <emphasis>must not</emphasis> attempt to free or modify the return value.
+ </para>
+
+ <para>
+ Entries include but are not limited to:
+ <variablelist id="libpq-enum-PGlibInfoEntryKey" xreflabel="enum PGlibInfoEntryKey">
+ <title><type>PGlibInfoEntryKey</type></title>
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_END_OF_LIST</literal></term>
+ <listitem><para>List terminator. Do not read past this point. Always has value 0.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_END_OF_LIST</literal></term>
+ <listitem><para>List terminator. Do not read past this point. Always has value 0.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_VERSION_NUM</literal></term>
+ <listitem><para>Same as <function>PQlibVersion()</function></para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_VERSION_STR</literal></term>
+ <listitem><para>
+ Version string for the PostgreSQL this libpq came from, in exactly
+ the same format as the <literal>PG_VERSION_STR</literal> macro or
+ <xref linkend="guc-server-version-num"/> server variable. This
+ value is the version of the actually loaded libpq, not
+ the the application was compiled against or the version of
+ the postgres server being connected to.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_CONFIGURE_ARGS</literal></term>
+ <listitem><para>
+ Arguments passed to the <literal>configure</literal>
+ utility when the PostgreSQL this libpq came from was compiled.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_USE_SSL</literal></term>
+ <listitem><para>
+ 1 if this libpq was compiled with SSL/TLS support, otherwise 0.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_ENABLE_GSS</literal></term>
+ <listitem><para>
+ 1 if this libpq was compiled with GSSAPI (Kerberos) support, otherwise 0.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_ENABLE_THREAD_SAFETY</literal></term>
+ <listitem><para>
+ 1 if this libpq was compiled with thread safety enabled, otherwise 0,
+ like <xref linkend="libpq-PQisthreadsafe"/>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_HAVE_UNIX_SOCKETS</literal></term>
+ <listitem><para>
+ 1 if this libpq was compiled with unix socket support, otherwise 0.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_DEFAULT_PGSOCKET_DIR</literal></term>
+ <listitem><para>
+ The default unix socket directory this libpq will use when no
+ <literal>host</literal> is provided in a connection string.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>PGRES_LIBINFO_DEF_PGPORT</literal></term>
+ <listitem><para>
+ The default TCP or unix socket port that libpq will use if no
+ <literal>port</literal> is provided in a connection string.
+ </para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </para>
+
+ <note>
+ <para>
+ This function appeared in <productname>PostgreSQL</productname> version 14.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQlibInfoPrint">
+ <term><function>PQlibInfoPrint</function><indexterm
+ ><primary>PQlibInfoPrint</primary><seealso>PQlibInfo</seealso></indexterm></term>
+ <listitem>
+ <para>
+ Writes libpq version and configuration information to stdout.
+<synopsis>
+/* libpq-version.h */
+void PQlibInfoPrint(void);
+</synopsis>
+ </para>
+ <para>
+ This function prints the <structfield>key_str</structfield> and
+ <structfield>value_str</structfield> of each
+ <xref linkend="libpq-struct-PGlibInfoEntry"/>
+ returned by <xref linkend="libpq-PQlibInfo"/> to the application's
+ default stdio output stream (stdout).
+ </para>
+ <note>
+ <para>
+ This function appeared in <productname>PostgreSQL</productname> version 14.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect1>
@@ -7998,7 +8191,7 @@ void PQinitSSL(int do_ssl);
</para>
<variablelist>
- <varlistentry id="libpq-PQisthreadsafe">
+ <varlistentry id="libpq-PQisthreadsafe" xreflabel="PQisthreadsafe()">
<term><function>PQisthreadsafe</function><indexterm><primary>PQisthreadsafe</primary></indexterm></term>
<listitem>
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 4ac5f4b340..8a9a6f4c09 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -42,6 +42,7 @@ OBJS = \
fe-secure.o \
legacy-pqsignal.o \
libpq-events.o \
+ libpq-version.o \
pqexpbuffer.o \
fe-auth.o
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index bbc1f90481..1d8600f0d8 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -179,3 +179,5 @@ PQgetgssctx 176
PQsetSSLKeyPassHook_OpenSSL 177
PQgetSSLKeyPassHook_OpenSSL 178
PQdefaultSSLKeyPassHook_OpenSSL 179
+PQlibInfo 180
+PQlibInfoPrint 181
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 3b6a9fbce3..d9594cfa9b 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -594,7 +594,11 @@ extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
/* === in fe-misc.c === */
-/* Get the version of the libpq library in use */
+/*
+ * Get the version of the libpq library in use (PG_VERSION_NUM).
+ *
+ * See libpq-version.h for more detailed library information.
+ */
extern int PQlibVersion(void);
/* Determine length of multibyte encoded char at *s */
diff --git a/src/interfaces/libpq/libpq-version.c b/src/interfaces/libpq/libpq-version.c
new file mode 100644
index 0000000000..51a5c608c8
--- /dev/null
+++ b/src/interfaces/libpq/libpq-version.c
@@ -0,0 +1,124 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-version.c
+ * Functions to report libpq version, configuration and options
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/interfaces/libpq/fe-version.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <stdio.h>
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "libpq-version.h"
+
+/* Adapt defined(foo) to is_foo boolean-truth */
+#ifdef USE_SSL
+#define is_use_ssl 1
+#else
+#define is_use_ssl 0
+#endif
+#ifdef ENABLE_GSS
+#define is_enable_gss 1
+#else
+#define is_enable_gss 0
+#endif
+#ifdef ENABLE_THREAD_SAFETY
+#define is_enable_thread_safety 1
+#else
+#define is_enable_thread_safety 0
+#endif
+#ifdef HAVE_UNIX_SOCKETS
+#define is_have_unix_sockets 1
+#else
+#define is_have_unix_sockets 0
+#ifndef DEFAULT_PGSOCKET_DIR
+#define DEFAULT_PGSOCKET_DIR gettext_noop("")
+#endif
+#endif
+
+#define libinfo_boolstr(x) (x) == 1 ? gettext_noop("1") : gettext_noop("0")
+
+static const PGlibInfoEntry PGlibInfoEntries[] = {
+ {
+ PGRES_LIBINFO_VERSION_NUM,
+ gettext_noop("VERSION_NUM"),
+ CppAsString2(PG_VERSION_NUM), 1, PG_VERSION_NUM
+ },
+ {
+ PGRES_LIBINFO_VERSION_STR,
+ gettext_noop("VERSION"),
+ PG_VERSION_STR, 0, 0
+ },
+ {
+ PGRES_LIBINFO_CONFIGURE_ARGS,
+ gettext_noop("CONFIGURE_ARGS"),
+ CONFIGURE_ARGS, 0, 0
+ },
+ {
+ PGRES_LIBINFO_USE_SSL,
+ gettext_noop("USE_SSL"),
+ libinfo_boolstr(is_use_ssl), 1, is_use_ssl
+ },
+ {
+ PGRES_LIBINFO_ENABLE_GSS,
+ gettext_noop("ENABLE_GSS"),
+ libinfo_boolstr(is_enable_gss), 1, is_enable_gss
+ },
+ {
+ PGRES_LIBINFO_ENABLE_THREAD_SAFETY,
+ gettext_noop("ENABLE_THREAD_SAFETY"),
+ libinfo_boolstr(is_enable_thread_safety),
+ 1, is_enable_thread_safety
+ },
+ {
+ PGRES_LIBINFO_HAVE_UNIX_SOCKETS,
+ gettext_noop("HAVE_UNIX_SOCKETS"),
+ libinfo_boolstr(is_have_unix_sockets),
+ 1, is_have_unix_sockets
+ },
+ {
+ PGRES_LIBINFO_DEFAULT_PGSOCKET_DIR,
+ gettext_noop("DEFAULT_PGSOCKET_DIR"),
+ DEFAULT_PGSOCKET_DIR, 0, 0
+ },
+ {
+ PGRES_LIBINFO_DEF_PGPORT,
+ gettext_noop("DEF_PGPORT"),
+ DEF_PGPORT_STR, 1, DEF_PGPORT
+ },
+ /* Sentry value, must always be at end of list */
+ { PGRES_LIBINFO_END_OF_LIST, NULL, 0, 0 }
+};
+
+/* Get key/value library configuration info for apps */
+const PGlibInfoEntry *
+PQlibInfo(void)
+{
+ return &PGlibInfoEntries[0];
+}
+
+/*
+ * Dump PGlibInfoEntries to stdout. This deliberately does not take a FILE*
+ * because it is not guaranteed to be safe to pass a FILE* across shared
+ * library boundaries on Windows or anywhere else that libpq might be linked to
+ * a different C runtime than the executable that loaded it .
+ */
+void
+PQlibInfoPrint(void)
+{
+ const PGlibInfoEntry * ientry = PQlibInfo();
+ while (ientry->key != PGRES_LIBINFO_END_OF_LIST)
+ {
+ fprintf(stdout, "%s: %s\n", ientry->key_name, ientry->value_str);
+ ientry ++;
+ }
+}
diff --git a/src/interfaces/libpq/libpq-version.h b/src/interfaces/libpq/libpq-version.h
new file mode 100644
index 0000000000..d38af8fa9a
--- /dev/null
+++ b/src/interfaces/libpq/libpq-version.h
@@ -0,0 +1,46 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq-version.h
+ * This file provides an interface to inspect the version and capabilities
+ * of the currently linked-to libpq at runtime.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq/libpq-version.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+/* See the main documentation for info on these keys */
+typedef enum PGlibInfoEntryKey
+{
+ PGRES_LIBINFO_END_OF_LIST = 0,
+ PGRES_LIBINFO_VERSION_NUM,
+ PGRES_LIBINFO_VERSION_STR,
+ PGRES_LIBINFO_CONFIGURE_ARGS,
+ PGRES_LIBINFO_USE_SSL,
+ PGRES_LIBINFO_ENABLE_GSS,
+ PGRES_LIBINFO_ENABLE_THREAD_SAFETY,
+ PGRES_LIBINFO_HAVE_UNIX_SOCKETS,
+ PGRES_LIBINFO_DEFAULT_PGSOCKET_DIR,
+ PGRES_LIBINFO_DEF_PGPORT
+} PGlibInfoEntryKey;
+
+/* One entry from PQlibInfo() */
+typedef struct PGlibInfoEntry
+{
+ enum PGlibInfoEntryKey key;
+ const char * key_name;
+ const char * value_str;
+ bool has_int_value;
+ int value_int;
+} PGlibInfoEntry;
+
+/* Return statically allocated array of PQlibInfo terminated by null sentinel */
+extern const PGlibInfoEntry * PQlibInfo(void);
+
+/* Dump PQlibInfo output to stdout */
+extern void PQlibInfoPrint(void);
--
2.26.2
On 2020-Oct-26, Craig Ringer wrote:
Patch 0001 adds PQlibInfo(), which returns an array of key/value
description items reporting on configuration like the full version string,
SSL support, gssapi support, thread safety, default port and default unix
socket path. This is for application use and application diagnostics. It
also adds PQlibInfoPrint() which dumps PQlibInfo() keys/values to stdout.
See the commit message in patch 0001 for details.
Sounds useful. I'd have PQlibInfoPrint(FILE *) instead, so you can pass
stdout or whichever fd you want.
Patch 0002 exposes LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS symbols in the dynamic symbol table. These can be
accessed by a debugger even when the library cannot be loaded or executed,
and unlike macros are available even in a stripped executable. So they can
be used to identify a libpq binary found in the wild. Their storage is
shared with PQlibInfo()'s static data, so they only cost three symbol table
entries.
Interesting. Is this real-world useful? I'm thinking most of the time
I'd just run the library, but maybe you know of cases where that doesn't
work?
Patch 0003 allows libpq.so to be executed directly from the command line to
print its version, configure arguments etc exactly as PQlibInfoPrint()
would output them. This is only enabled on x64 linux for now but can be
extended to other targets quite simply.
+1 --- to me this is the bit that would be most useful, I expect.
At 2020-10-26 20:56:57 +0800, craig.ringer@enterprisedb.com wrote:
$ ./build/src/interfaces/libpq/libpq.so
VERSION_NUM: 140000
VERSION: PostgreSQL 14devel on x86_64-pc-linux-gnu, compiled by gcc (GCC)
10.2.1 20200723 (Red Hat 10.2.1-1), 64-bit
CONFIGURE_ARGS: '--cache-file=config.cache-'
'--prefix=/home/craig/pg/master' '--enable-debug' '--enable-cassert'
'--enable-tap-tests' '--enable-dtrace' 'CC=/usr/lib64/ccache/gcc'
'CFLAGS=-Og -ggdb3' 'CPPFLAGS=' 'CPP=/usr/lib64/ccache/gcc -E'
USE_SSL: 0
ENABLE_GSS: 0
ENABLE_THREAD_SAFETY: 1
HAVE_UNIX_SOCKETS: 1
DEFAULT_PGSOCKET_DIR: /tmp
DEF_PGPORT: 5432
This is excellent.
-- Abhijit
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
On 2020-Oct-26, Craig Ringer wrote:
also adds PQlibInfoPrint() which dumps PQlibInfo() keys/values to stdout.
Sounds useful. I'd have PQlibInfoPrint(FILE *) instead, so you can pass
stdout or whichever fd you want.
+1. Are we concerned about translatability of these strings? I think
I'd vote against, as it would complicate applications, but it's worth
thinking about it now not later.
Patch 0002 exposes LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS symbols in the dynamic symbol table. These can be
accessed by a debugger even when the library cannot be loaded or executed,
and unlike macros are available even in a stripped executable. So they can
be used to identify a libpq binary found in the wild. Their storage is
shared with PQlibInfo()'s static data, so they only cost three symbol table
entries.
Interesting. Is this real-world useful?
-1, I think this is making way too many assumptions about the content
and format of a shlib.
Patch 0003 allows libpq.so to be executed directly from the command line to
print its version, configure arguments etc exactly as PQlibInfoPrint()
would output them. This is only enabled on x64 linux for now but can be
extended to other targets quite simply.
+1 --- to me this is the bit that would be most useful, I expect.
Again, I'm not exactly excited about this. I do not one bit like
patches that assume that x64 linux is the universe, or at least
all of it that need be catered to. Reminds me of people who thought
Windows was the universe, not too many years ago.
I'd rather try to set this up so that some fairly standard tooling
like "strings" + "grep" can be used to pull out the info. Sure,
it would be less convenient, but honestly how often is this really
going to be necessary?
regards, tom lane
On Tue, Oct 27, 2020 at 12:41 AM Alvaro Herrera <alvherre@alvh.no-ip.org>
wrote:
On 2020-Oct-26, Craig Ringer wrote:
Patch 0001 adds PQlibInfo(), which returns an array of key/value
description items reporting on configuration like the full versionstring,
SSL support, gssapi support, thread safety, default port and default unix
socket path. This is for application use and application diagnostics. It
also adds PQlibInfoPrint() which dumps PQlibInfo() keys/values to stdout.
See the commit message in patch 0001 for details.Sounds useful. I'd have PQlibInfoPrint(FILE *) instead, so you can pass
stdout or whichever fd you want.
The decision not to do so was deliberate. On any platform where a shared
library could be linked to a different C runtime library than the main
executable or other libraries it is not safe to pass a FILE*. This is most
common on Windows.
I figured it's just a trivial wrapper anyway, so people can just write or
copy it if they really care.
Patch 0002 exposes LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS symbols in the dynamic symbol table. These can be
accessed by a debugger even when the library cannot be loaded orexecuted,
and unlike macros are available even in a stripped executable. So they
can
be used to identify a libpq binary found in the wild. Their storage is
shared with PQlibInfo()'s static data, so they only cost three symboltable
entries.
Interesting. Is this real-world useful? I'm thinking most of the time
I'd just run the library, but maybe you know of cases where that doesn't
work?
It was prompted by a support conversation about how to identify a libpq.
So I'd say yes.
In that case the eventual approach used was to use Python's ctypes to
dynamically load libpq then call PQlibVersion().
Patch 0003 allows libpq.so to be executed directly from the command line
toprint its version, configure arguments etc exactly as PQlibInfoPrint()
would output them. This is only enabled on x64 linux for now but can be
extended to other targets quite simply.+1 --- to me this is the bit that would be most useful, I expect.
It's also kinda cool.
But it's using a bit of a platform quirk that's not supported by the
toolchain as well as I'd really like - annoyingly, when you pass a
--entrypoint to GNU ld or to LLVM's ld.lld, it should really emit the
default .interp section to point to /bin/ld.so.2 or
/lib64/ld-linux-x86-64.so.2 as appropriate. But when building -shared they
don't seem to want to, nor do they expose a sensible macro that lets you
get the default string yourself.
So I thought there was a moderate to high chance that this patch would trip
someone's "yuck" meter.
On Tue, Oct 27, 2020 at 12:56 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
On 2020-Oct-26, Craig Ringer wrote:
also adds PQlibInfoPrint() which dumps PQlibInfo() keys/values to stdout.
Sounds useful. I'd have PQlibInfoPrint(FILE *) instead, so you can pass
stdout or whichever fd you want.+1. Are we concerned about translatability of these strings? I think
I'd vote against, as it would complicate applications, but it's worth
thinking about it now not later.
It's necessary not to translate the key names, they are identifiers
not descriptive text. I don't object to having translations too, but
the translation teams have quite enough to do already with user-facing
text that will get regularly seen. So while it'd be potentially
interesting to expose translated versions too, I'm not entirely
convinced. It's a bit like translating macro names. You could, but ...
why?
Patch 0002 exposes LIBPQ_VERSION_STR, LIBPQ_VERSION_NUM and
LIBPQ_CONFIGURE_ARGS symbols in the dynamic symbol table. These can be
accessed by a debugger even when the library cannot be loaded or executed,
and unlike macros are available even in a stripped executable. So they can
be used to identify a libpq binary found in the wild. Their storage is
shared with PQlibInfo()'s static data, so they only cost three symbol table
entries.Interesting. Is this real-world useful?
-1, I think this is making way too many assumptions about the content
and format of a shlib.
I'm not sure I understand what assumptions you're concerned about or
their consequences. On any ELF it should be just fine, and Mach-O
should be too. I do need to check that MSVC generates direct symbols
for WIN32 PE, not indirect thunked data symbols.
It doesn't help that I failed to supply the final revision of this
patch, which does this:
-const char * const LIBPQ_VERSION_STR = PG_VERSION_STR;
+const char LIBPQ_VERSION_STR[] = PG_VERSION_STR;
-const char * const LIBPQ_CONFIGURE_ARGS = CONFIGURE_ARGS;
+const char LIBPQ_CONFIGURE_ARGS[] = CONFIGURE_ARGS;
... to properly ensure the string symbols go into the read-only data section:
$ eu-nm --defined-only -D $LIBPQ | grep LIBPQ_
LIBPQ_CONFIGURE_ARGS |0000000000028640|GLOBAL|OBJECT
|00000000000000e4| libpq-version.c:74|.rodata
LIBPQ_VERSION_NUM |0000000000028620|GLOBAL|OBJECT
|0000000000000004| libpq-version.c:75|.rodata
LIBPQ_VERSION_STR |0000000000028740|GLOBAL|OBJECT
|000000000000006c| libpq-version.c:73|.rodata
I don't propose these to replace information functions or macros, I'm
suggesting we add them as an aid to tooling and for debugging. I have
had quite enough times when I've faced a mystery libpq, and it's not
always practical in a given target environment to just compile a tool
to print the version.
In addition to easy binary identification, having symbolic references
to the version info is useful for dynamic tracing tools like perf and
systemtap - they cannot execute functions directly in the target
address space, but they can read data symbols. I actually want to
expose matching symbols in postgres itself, for the use of dynamic
tracing utilities, so they can autodetect the target postgres at
runtime even without -ggdb3 level debuginfo with macros, and correctly
adapt to version specifics of the target postgres.
In terms of standard tooling here are some different ways you can get
this information symbolically.
$ LIBPQ=/path/to/libpq.so
$ gdb -batch -ex 'p (int) LIBPQ_VERSION_NUM' -ex 'p (const char *)
LIBPQ_VERSION_STR' $LIBPQ
$1 = 140000
$2 = "PostgreSQL 14devel on x86_64-pc-linux-gnu, compiled by gcc (GCC)
10.2.1 20200723 (Red Hat 10.2.1-1), 64-bit"
$ perl getpqver.pl $LIBPQ
LIBPQ_VERSION_NUM=140000
LIBPQ_VERSION_STR=PostgreSQL 14devel on x86_64-pc-linux-gnu, compiled
by gcc (GCC) 10.2.1 20200723 (Red Hat 10.2.1-1), 64-bit
I've attached getpqver.pl. It uses eu-nm from elfutils to get symbol
offset and length, which is pretty standard stuff. And it's quite
simple to adapt it to use legacy binutils "nm" by invoking
nm --dynamic --defined -S $LIBPQ
and tweaking the reader.
If you really want something strings-able, I'm sure that's reasonably
feasible, but I don't think it's particularly unreasonable to expect
to be able to inspect the symbol table using appropriate platform
tools or a simple debugger command.
Again, I'm not exactly excited about this. I do not one bit like
patches that assume that x64 linux is the universe, or at least
all of it that need be catered to. Reminds me of people who thought
Windows was the universe, not too many years ago.
Yeah. I figured you'd say that, and don't disagree. It's why I split
this patch out - it's kind of a sacrificial patch.
I actually wrote this part first.
Then I wrote PQlibInfo() when I realised that there was no sensible
pre-existing way to get the information I wanted to dump from libpq at
the API level, and adapted the executable .so output to call it.
I'd rather try to set this up so that some fairly standard tooling
like "strings" + "grep" can be used to pull out the info. Sure,
it would be less convenient, but honestly how often is this really
going to be necessary?
eu-readelf and objdump are pretty standard tooling. But I really don't
much care if the executable .so hack gets in, it's mostly a fun PoC.
If you can execute libpq then the dynamic linker must be able to load
it and resolve its symbols, in which case you can probably just as
easily do this:
python -c "import sys, ctypes;
ctypes.cdll.LoadLibrary(sys.argv[1]).PQlibInfoPrint()"
build/src/interfaces/libpq/libpq.so
or compile and run a trivial C one-liner.
As much as anything I thought it was a good way to stimulate
discussion and give you something easy to reject ;)
Attachments:
On 2020-Oct-27, Craig Ringer wrote:
On Tue, Oct 27, 2020 at 12:56 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
+1. Are we concerned about translatability of these strings? I think
I'd vote against, as it would complicate applications, but it's worth
thinking about it now not later.It's necessary not to translate the key names, they are identifiers
not descriptive text. I don't object to having translations too, but
the translation teams have quite enough to do already with user-facing
text that will get regularly seen. So while it'd be potentially
interesting to expose translated versions too, I'm not entirely
convinced. It's a bit like translating macro names. You could, but ...
why?
I don't think translating these values is useful for much. I see it
similar to translating pg_controldata output: it is troublesome (to
pg_upgrade for instance) and serves no public that I know of.
Again, I'm not exactly excited about this. I do not one bit like
patches that assume that x64 linux is the universe, or at least
all of it that need be catered to. Reminds me of people who thought
Windows was the universe, not too many years ago.Yeah. I figured you'd say that, and don't disagree. It's why I split
this patch out - it's kind of a sacrificial patch.
Well, if we can make it run in more systems than just Linux, then it
seems worth having. The submitted patch seems a little bit on the
naughty side.
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
Well, if we can make it run in more systems than just Linux, then it
seems worth having. The submitted patch seems a little bit on the
naughty side.
I agree that the facility seems possibly useful, as long as we can
minimize its platform dependency. Just embedding some strings, as
I suggested upthread, seems like it'd go far in that direction.
Yeah, you could spend a lot of effort to make it a bit more user
friendly, but is the effort really going to be repaid? The use
case for this isn't that large, I don't think.
regards, tom lane
On Tue, Nov 10, 2020 at 12:33 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
Well, if we can make it run in more systems than just Linux, then it
seems worth having. The submitted patch seems a little bit on the
naughty side.I agree that the facility seems possibly useful, as long as we can
minimize its platform dependency. Just embedding some strings, as
I suggested upthread, seems like it'd go far in that direction.
Yeah, you could spend a lot of effort to make it a bit more user
friendly, but is the effort really going to be repaid? The use
case for this isn't that large, I don't think.
The reason I want to expose symbols is mainly for tracing tooling - perf,
systemtap, etc.
I thought it'd make sense to also provide another way to identify the libpq
binary.
My other hesitation about using a "strings libpq.so" approach is that it's
not something I'd be super happy about automating and relying on in scripts
etc. It could break depending on how the compiler decides to arrange things
or due to unrelated changes in libpq that create similar-looking strings
later. I'd prefer to do it deterministically. You can already use "strings"
to identify an unstripped binary built with -ggdb3 (macros in DWARF
debuginfo), but we don't compile the PG_VERSION into the binary, so you
can't even get the basic version string like "postgres (PostgreSQL) 11.9"
from 'strings'.
The whole PQlibInfo() thing came about because I thought it'd be
potentially useful. I've had issues before with applications being built
against a newer version of libpq than what they're linked against, and it's
been rather frustrating to make the app tolerant of that. But it can be
solved (clumsily) using various existing workarounds.
The main things I'd really like to get in place are a way to get the
version as an ELF data symbol, and a simple way to ID the binary.
So the minimal change would be to declare:
const char LIBPQ_VERSION_STR[] = PG_VERSION_STR;
const int LIBPQ_VERSION_NUM = PG_VERSION_NUM;
then change PQgetVersion() to return LIBPQ_VERSION_NUM and add a
PQgetVersionStr() that returns LIBPQ_VERSION_STR.
That OK with you?
On Tue, Nov 10, 2020 at 2:22 PM Craig Ringer <craig.ringer@enterprisedb.com>
wrote:
The main things I'd really like to get in place are a way to get the
version as an ELF data symbol, and a simple way to ID the binary.So the minimal change would be to declare:
const char LIBPQ_VERSION_STR[] = PG_VERSION_STR;
const int LIBPQ_VERSION_NUM = PG_VERSION_NUM;then change PQgetVersion() to return LIBPQ_VERSION_NUM and add a
PQgetVersionStr() that returns LIBPQ_VERSION_STR.That OK with you?
Proposed minimal patch attached.
Attachments:
v2-0001-Add-PQlibVersionString-to-libpq.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Add-PQlibVersionString-to-libpq.patchDownload
From 137fa6f0ee8a13e56025af4adf7707b8ec8e4739 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig.ringer@2ndquadrant.com>
Date: Wed, 11 Nov 2020 11:45:05 +0800
Subject: [PATCH v2] Add PQlibVersionString() to libpq
Provide applications with a way to get the full postgres version string
associated with the libpq they are dynamically linked against. This might
not be the same libpq they were compiled against, so checking PG_VERSION_STR
is not the same thing.
Expose LIBPQ_VERSION_NUM and LIBPQ_VERSION_STR symbols in the libpq export
symbol table for examination in situations like core file analysis where
functions cannot be called. Debuggers can't inspect PG_VERSION_NUM etc unless
-ggdb3 debuginfo is available, wheras this will will work even with a stripped
binary since they're in the export symbol table. Also useful for use in trace
tooling (dtrace, systemtap, perf, etc) and for identifying a stray libpq binary
found lying around:
$ gdb -batch \
-ex 'p (int)LIBPQ_VERSION_NUM' \
-ex 'p (const char*)&LIBPQ_VERSION_STR' \
build/src/interfaces/libpq/libpq.so
$1 = 140000
$2 = 0x269c0 <LIBPQ_VERSION_STR> "PostgreSQL 14devel ..."
or
$ strings build/src/interfaces/libpq/libpq.so | egrep '^PostgreSQL [0-9]'
PostgreSQL 14devel ...
---
doc/src/sgml/libpq.sgml | 49 ++++++++++++++++++++++++++++++++
src/interfaces/libpq/exports.txt | 3 ++
src/interfaces/libpq/fe-misc.c | 21 +++++++++++++-
src/interfaces/libpq/libpq-fe.h | 5 +++-
src/interfaces/libpq/libpq-int.h | 8 ++++++
5 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 9ce32fb39b..1b7f57c549 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -6248,6 +6248,55 @@ int PQlibVersion(void);
on version 9.1 or later.
</para>
</note>
+
+ <para>
+ The value returned by <function>PQlibVersion()</function> is also exposed as the
+ exported symbol <varname>LIBPQ_VERSION_NUM</varname> for use by debuggers and
+ trace tooling. Applications should not reference this directly; call the function
+ instead.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQlibVersionString">
+ <term>
+ <function>PQlibVersionString</function>
+ <indexterm>
+ <primary>PQlibVersionString</primary>
+ <seealso>PQserverVersion</seealso>
+ <seealso>PQlibVersion</seealso>
+ </indexterm>
+ </term>
+
+ <listitem>
+ <para>
+ Return a string describing the PostgreSQL version that the currently loaded
+ <productname>libpq</productname> binary was compiled against.
+<synopsis>
+const char * PQlibVersion(void);
+</synopsis>
+ </para>
+
+ <para>
+ The return value is a statically allocated string. The caller must not free it.
+ </para>
+
+ <para>
+ Applications should prefer to call <function>PQlibVersion()</function>
+ to determine the major and minor versions. Do not parse this version string
+ to get the numeric version information. Use <function>PQlibVersionString</function>
+ when displaying a detailed version in diagnostic output or debug logs where
+ a more detailed identification of the exact libpq binary may be desirable.
+ </para>
+
+ <para>
+ The value returned by <function>PQlibVersionString()</function> is also exposed as the
+ exported symbol <varname>LIBPQ_VERSION_STR</varname> for use by debuggers and
+ trace tooling. Applications should not reference this directly; call the function
+ instead.
+ </para>
+
</listitem>
</varlistentry>
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index bbc1f90481..76dfdc1d5f 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -179,3 +179,6 @@ PQgetgssctx 176
PQsetSSLKeyPassHook_OpenSSL 177
PQgetSSLKeyPassHook_OpenSSL 178
PQdefaultSSLKeyPassHook_OpenSSL 179
+PQlibVersionString 180
+LIBPQ_VERSION_STR 181
+LIBPQ_VERSION_NUM 182
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 4ffc7f33fb..c57ef85a4d 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -59,15 +59,34 @@ static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
time_t end_time);
static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
+/*
+ * Storage for version information. These are deliberately not static, so they
+ * appear in the symbol table where they can be inspected by debuggers and
+ * trace tools in contexts where a function call isn't possible. They aren't
+ * declared as externs in libpq-fe.h because we want programs to use the
+ * function interfaces instead.
+ */
+const char LIBPQ_VERSION_STR[] = PG_VERSION_STR;
+const int LIBPQ_VERSION_NUM = PG_VERSION_NUM;
+
/*
* PQlibVersion: return the libpq version number
*/
int
PQlibVersion(void)
{
- return PG_VERSION_NUM;
+ return LIBPQ_VERSION_NUM;
}
+/*
+ * PQlibVersionString: return the postgres version that this libpq
+ * was compiled against.
+ */
+const char *
+PQlibVersionString(void)
+{
+ return &LIBPQ_VERSION_STR[0];
+}
/*
* pqGetc: get 1 character from the connection
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 3b6a9fbce3..9f61cdc1ed 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -594,9 +594,12 @@ extern int lo_export(PGconn *conn, Oid lobjId, const char *filename);
/* === in fe-misc.c === */
-/* Get the version of the libpq library in use */
+/* Get the version of the libpq library in use (PG_VERSION_NUM) */
extern int PQlibVersion(void);
+/* Get the postgres version string for this libpq build (PG_VERSION_STR) */
+extern const char * PQlibVersionString(void);
+
/* Determine length of multibyte encoded char at *s */
extern int PQmblen(const char *s, int encoding);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 1de91ae295..e3a970bf11 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -820,4 +820,12 @@ extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigne
#define SOCK_ERRNO_SET(e) (errno = (e))
#endif
+/*
+ * Embed libpq version information symbols. Client code should use
+ * PQlibVersion() and PQlibVersionString() instead. These are here for
+ * debug/trace tooling and diagnostic purposes.
+ */
+extern const char LIBPQ_VERSION_STR[]; /* libpq compile-time PG_VERSION_STR */
+extern const int LIBPQ_VERSION_NUM; /* libpq compile-time PG_VERSION_NUM */
+
#endif /* LIBPQ_INT_H */
--
2.26.2
Hi Craig,
On Wed, Nov 11, 2020 at 1:11 PM Craig Ringer
<craig.ringer@enterprisedb.com> wrote:
On Tue, Nov 10, 2020 at 2:22 PM Craig Ringer <craig.ringer@enterprisedb.com> wrote:
The main things I'd really like to get in place are a way to get the version as an ELF data symbol, and a simple way to ID the binary.
So the minimal change would be to declare:
const char LIBPQ_VERSION_STR[] = PG_VERSION_STR;
const int LIBPQ_VERSION_NUM = PG_VERSION_NUM;then change PQgetVersion() to return LIBPQ_VERSION_NUM and add a PQgetVersionStr() that returns LIBPQ_VERSION_STR.
That OK with you?
Proposed minimal patch attached.
You sent in your patch, v2-0001-Add-PQlibVersionString-to-libpq.patch
to pgsql-hackers on Nov 11, but you did not post it to the next
CommitFest[1]https://commitfest.postgresql.org/31/. If this was intentional, then you need to take no
action. However, if you want your patch to be reviewed as part of the
upcoming CommitFest, then you need to add it yourself before
2021-01-01 AOE[2]https://en.wikipedia.org/wiki/Anywhere_on_Earth. Thanks for your contributions.
Regards,
[1]: https://commitfest.postgresql.org/31/
[2]: https://en.wikipedia.org/wiki/Anywhere_on_Earth
--
Masahiko Sawada
EnterpriseDB: https://www.enterprisedb.com/