On Tue, Nov 10, 2020 at 2:22 PM Craig Ringer <craig.rin...@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.
From 137fa6f0ee8a13e56025af4adf7707b8ec8e4739 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig.rin...@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

Reply via email to