]> granicus.if.org Git - postgresql/commitdiff
Add libpq function PQconninfo()
authorMagnus Hagander <magnus@hagander.net>
Fri, 30 Nov 2012 06:09:18 +0000 (15:09 +0900)
committerMagnus Hagander <magnus@hagander.net>
Fri, 30 Nov 2012 06:11:08 +0000 (15:11 +0900)
This allows a caller to get back the exact conninfo array that was
used to create a connection, including parameters read from the
environment.

In doing this, restructure how options are copied from the conninfo
to the actual connection.

Zoltan Boszormenyi and Magnus Hagander

doc/src/sgml/libpq.sgml
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-fe.h

index 255c5c1abb84127abfa8f7df9bc9a76a9c11fb19..e7ad066fe515b83e73e36a669b07309887cb1f15 100644 (file)
@@ -496,6 +496,30 @@ typedef struct
      </listitem>
     </varlistentry>
 
+    <varlistentry id="libpq-pqconninfo">
+     <term><function>PQconninfo</function><indexterm><primary>PQconninfo</></></term>
+     <listitem>
+      <para>
+       Returns the connection options used by a live connection.
+<synopsis>
+PQconninfoOption *PQconninfo(PGconn *conn);
+</synopsis>
+      </para>
+
+      <para>
+       Returns a connection options array.  This can be used to determine
+       all possible <function>PQconnectdb</function> options and the
+       values that were used to connect to the server. The return
+       value points to an array of <structname>PQconninfoOption</structname>
+       structures, which ends with an entry having a null <structfield>keyword</>
+       pointer. All notes above for <function>PQconndefaults</function> also
+       apply to the result of <function>PQconninfo</function>.
+      </para>
+
+     </listitem>
+    </varlistentry>
+
+
     <varlistentry id="libpq-pqconninfoparse">
      <term><function>PQconninfoParse</function><indexterm><primary>PQconninfoParse</></></term>
      <listitem>
index 56d0bb8dc5816ee128fc8234f95585aca20ebdea..93da50df31143713f38537fbe667e5d7fe48fc94 100644 (file)
@@ -164,3 +164,4 @@ PQsetSingleRowMode        161
 lo_lseek64                162
 lo_tell64                 163
 lo_truncate64             164
+PQconninfo                165
index 1386bb791a96fab087af1ba33546ab89772327fd..beee8172a0237f1b6cd563b8a6857362d44adb4b 100644 (file)
@@ -137,81 +137,112 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
  * PQconninfoOptions[] *must* be NULL. In a working copy, non-null "val"
  * fields point to malloc'd strings that should be freed when the working
  * array is freed (see PQconninfoFree).
+ *
+ * The first part of each struct is identical to the one in libpq-fe.h,
+ * which is required since we memcpy() data between the two!
  * ----------
  */
-static const PQconninfoOption PQconninfoOptions[] = {
+typedef struct _internalPQconninfoOption
+{
+       char       *keyword;            /* The keyword of the option                    */
+       char       *envvar;                     /* Fallback environment variable name   */
+       char       *compiled;           /* Fallback compiled in default value   */
+       char       *val;                        /* Option's current value, or NULL               */
+       char       *label;                      /* Label for field in connect dialog    */
+       char       *dispchar;           /* Indicates how to display this field in a
+                                                                * connect dialog. Values are: "" Display
+                                                                * entered value as is "*" Password field -
+                                                                * hide value "D"  Debug option - don't show
+                                                                * by default */
+       int                     dispsize;               /* Field size in characters for dialog  */
+       /* ---
+        * Anything above this comment must be synchronized with
+        * PQconninfoOption in libpq-fe.h, since we memcpy() data
+        * between them!
+        * ---
+        */
+       off_t           connofs;                /* Offset into PGconn struct, -1 if not there */
+}      internalPQconninfoOption;
+
+static const internalPQconninfoOption PQconninfoOptions[] = {
        /*
         * "authtype" is no longer used, so mark it "don't show".  We keep it in
         * the array so as not to reject conninfo strings from old apps that might
         * still try to set it.
         */
        {"authtype", "PGAUTHTYPE", DefaultAuthtype, NULL,
-       "Database-Authtype", "D", 20},
+       "Database-Authtype", "D", 20, -1},
 
        {"service", "PGSERVICE", NULL, NULL,
-       "Database-Service", "", 20},
+       "Database-Service", "", 20, -1},
 
        {"user", "PGUSER", NULL, NULL,
-       "Database-User", "", 20},
+               "Database-User", "", 20,
+       offsetof(struct pg_conn, pguser)},
 
        {"password", "PGPASSWORD", NULL, NULL,
-       "Database-Password", "*", 20},
+               "Database-Password", "*", 20,
+       offsetof(struct pg_conn, pgpass)},
 
        {"connect_timeout", "PGCONNECT_TIMEOUT", NULL, NULL,
-       "Connect-timeout", "", 10}, /* strlen(INT32_MAX) == 10 */
+               "Connect-timeout", "", 10,              /* strlen(INT32_MAX) == 10 */
+       offsetof(struct pg_conn, connect_timeout)},
 
        {"dbname", "PGDATABASE", NULL, NULL,
-       "Database-Name", "", 20},
+               "Database-Name", "", 20,
+       offsetof(struct pg_conn, dbName)},
 
        {"host", "PGHOST", NULL, NULL,
-       "Database-Host", "", 40},
+               "Database-Host", "", 40,
+       offsetof(struct pg_conn, pghost)},
 
        {"hostaddr", "PGHOSTADDR", NULL, NULL,
-       "Database-Host-IP-Address", "", 45},
+               "Database-Host-IP-Address", "", 45,
+       offsetof(struct pg_conn, pghostaddr)},
 
        {"port", "PGPORT", DEF_PGPORT_STR, NULL,
-       "Database-Port", "", 6},
+               "Database-Port", "", 6,
+       offsetof(struct pg_conn, pgport)},
 
        {"client_encoding", "PGCLIENTENCODING", NULL, NULL,
-       "Client-Encoding", "", 10},
+               "Client-Encoding", "", 10,
+       offsetof(struct pg_conn, client_encoding_initial)},
 
        /*
         * "tty" is no longer used either, but keep it present for backwards
         * compatibility.
         */
        {"tty", "PGTTY", DefaultTty, NULL,
-       "Backend-Debug-TTY", "D", 40},
+               "Backend-Debug-TTY", "D", 40,
+       offsetof(struct pg_conn, pgtty)},
 
        {"options", "PGOPTIONS", DefaultOption, NULL,
-       "Backend-Debug-Options", "D", 40},
+               "Backend-Debug-Options", "D", 40,
+       offsetof(struct pg_conn, pgoptions)},
 
        {"application_name", "PGAPPNAME", NULL, NULL,
-       "Application-Name", "", 64},
+               "Application-Name", "", 64,
+       offsetof(struct pg_conn, appname)},
 
        {"fallback_application_name", NULL, NULL, NULL,
-       "Fallback-Application-Name", "", 64},
+               "Fallback-Application-Name", "", 64,
+       offsetof(struct pg_conn, fbappname)},
 
        {"keepalives", NULL, NULL, NULL,
-       "TCP-Keepalives", "", 1},       /* should be just '0' or '1' */
+               "TCP-Keepalives", "", 1,        /* should be just '0' or '1' */
+       offsetof(struct pg_conn, keepalives)},
 
        {"keepalives_idle", NULL, NULL, NULL,
-       "TCP-Keepalives-Idle", "", 10},         /* strlen(INT32_MAX) == 10 */
+               "TCP-Keepalives-Idle", "", 10,  /* strlen(INT32_MAX) == 10 */
+       offsetof(struct pg_conn, keepalives_idle)},
 
        {"keepalives_interval", NULL, NULL, NULL,
-       "TCP-Keepalives-Interval", "", 10}, /* strlen(INT32_MAX) == 10 */
+               "TCP-Keepalives-Interval", "", 10,              /* strlen(INT32_MAX) == 10 */
+       offsetof(struct pg_conn, keepalives_interval)},
 
        {"keepalives_count", NULL, NULL, NULL,
-       "TCP-Keepalives-Count", "", 10},        /* strlen(INT32_MAX) == 10 */
-
-#ifdef USE_SSL
-
-       /*
-        * "requiressl" is deprecated, its purpose having been taken over by
-        * "sslmode". It remains for backwards compatibility.
-        */
-       {"requiressl", "PGREQUIRESSL", "0", NULL,
-       "Require-SSL", "D", 1},
-#endif
+               "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
+       offsetof(struct pg_conn, keepalives_count)},
 
        /*
         * ssl options are allowed even without client SSL support because the
@@ -220,30 +251,38 @@ static const PQconninfoOption PQconninfoOptions[] = {
         * to exclude them since none of them are mandatory.
         */
        {"sslmode", "PGSSLMODE", DefaultSSLMode, NULL,
-       "SSL-Mode", "", 8},                     /* sizeof("disable") == 8 */
+               "SSL-Mode", "", 8,              /* sizeof("disable") == 8 */
+       offsetof(struct pg_conn, sslmode)},
 
        {"sslcompression", "PGSSLCOMPRESSION", "1", NULL,
-       "SSL-Compression", "", 1},
+               "SSL-Compression", "", 1,
+       offsetof(struct pg_conn, sslcompression)},
 
        {"sslcert", "PGSSLCERT", NULL, NULL,
-       "SSL-Client-Cert", "", 64},
+               "SSL-Client-Cert", "", 64,
+       offsetof(struct pg_conn, sslcert)},
 
        {"sslkey", "PGSSLKEY", NULL, NULL,
-       "SSL-Client-Key", "", 64},
+               "SSL-Client-Key", "", 64,
+       offsetof(struct pg_conn, sslkey)},
 
        {"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
-       "SSL-Root-Certificate", "", 64},
+               "SSL-Root-Certificate", "", 64,
+       offsetof(struct pg_conn, sslrootcert)},
 
        {"sslcrl", "PGSSLCRL", NULL, NULL,
-       "SSL-Revocation-List", "", 64},
+               "SSL-Revocation-List", "", 64,
+       offsetof(struct pg_conn, sslcrl)},
 
        {"requirepeer", "PGREQUIREPEER", NULL, NULL,
-       "Require-Peer", "", 10},
+               "Require-Peer", "", 10,
+       offsetof(struct pg_conn, requirepeer)},
 
 #if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
        /* Kerberos and GSSAPI authentication support specifying the service name */
        {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
-       "Kerberos-service-name", "", 20},
+               "Kerberos-service-name", "", 20,
+       offsetof(struct pg_conn, krbsrvname)},
 #endif
 
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
@@ -253,11 +292,13 @@ static const PQconninfoOption PQconninfoOptions[] = {
         * default
         */
        {"gsslib", "PGGSSLIB", NULL, NULL,
-       "GSS-library", "", 7},          /* sizeof("gssapi") = 7 */
+               "GSS-library", "", 7,   /* sizeof("gssapi") = 7 */
+       offsetof(struct pg_conn, gsslib)},
 #endif
 
        {"replication", NULL, NULL, NULL,
-       "Replication", "D", 5},
+               "Replication", "D", 5,
+       offsetof(struct pg_conn, replication)},
 
        /* Terminating entry --- MUST BE LAST */
        {NULL, NULL, NULL, NULL,
@@ -627,7 +668,7 @@ PQconnectStart(const char *conninfo)
 static void
 fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
 {
-       const char *tmp;
+       const internalPQconninfoOption *option;
 
        /*
         * Move option values into conn structure
@@ -637,72 +678,19 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions)
         *
         * XXX: probably worth checking strdup() return value here...
         */
-       tmp = conninfo_getval(connOptions, "hostaddr");
-       conn->pghostaddr = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "host");
-       conn->pghost = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "port");
-       conn->pgport = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "tty");
-       conn->pgtty = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "options");
-       conn->pgoptions = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "application_name");
-       conn->appname = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "fallback_application_name");
-       conn->fbappname = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "dbname");
-       conn->dbName = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "user");
-       conn->pguser = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "password");
-       conn->pgpass = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "connect_timeout");
-       conn->connect_timeout = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "client_encoding");
-       conn->client_encoding_initial = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "keepalives");
-       conn->keepalives = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "keepalives_idle");
-       conn->keepalives_idle = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "keepalives_interval");
-       conn->keepalives_interval = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "keepalives_count");
-       conn->keepalives_count = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslmode");
-       conn->sslmode = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslcompression");
-       conn->sslcompression = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslkey");
-       conn->sslkey = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslcert");
-       conn->sslcert = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslrootcert");
-       conn->sslrootcert = tmp ? strdup(tmp) : NULL;
-       tmp = conninfo_getval(connOptions, "sslcrl");
-       conn->sslcrl = tmp ? strdup(tmp) : NULL;
-#ifdef USE_SSL
-       tmp = conninfo_getval(connOptions, "requiressl");
-       if (tmp && tmp[0] == '1')
+       for (option = PQconninfoOptions; option->keyword; option++)
        {
-               /* here warn that the requiressl option is deprecated? */
-               if (conn->sslmode)
-                       free(conn->sslmode);
-               conn->sslmode = strdup("require");
+               const char *tmp = conninfo_getval(connOptions, option->keyword);
+
+               if (tmp && option->connofs >= 0)
+               {
+                       char      **connmember = (char **) ((char *) conn + option->connofs);
+
+                       if (*connmember)
+                               free(*connmember);
+                       *connmember = tmp ? strdup(tmp) : NULL;
+               }
        }
-#endif
-       tmp = conninfo_getval(connOptions, "requirepeer");
-       conn->requirepeer = tmp ? strdup(tmp) : NULL;
-#if defined(KRB5) || defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-       tmp = conninfo_getval(connOptions, "krbsrvname");
-       conn->krbsrvname = tmp ? strdup(tmp) : NULL;
-#endif
-#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
-       tmp = conninfo_getval(connOptions, "gsslib");
-       conn->gsslib = tmp ? strdup(tmp) : NULL;
-#endif
-       tmp = conninfo_getval(connOptions, "replication");
-       conn->replication = tmp ? strdup(tmp) : NULL;
 }
 
 /*
@@ -4020,15 +4008,29 @@ static PQconninfoOption *
 conninfo_init(PQExpBuffer errorMessage)
 {
        PQconninfoOption *options;
+       PQconninfoOption *opt_dest;
+       const internalPQconninfoOption *cur_opt;
 
-       options = (PQconninfoOption *) malloc(sizeof(PQconninfoOptions));
+       /*
+        * Get enough memory for all options in PQconninfoOptions, even if some
+        * end up being filtered out.
+        */
+       options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) / sizeof(PQconninfoOptions[0]));
        if (options == NULL)
        {
                printfPQExpBuffer(errorMessage,
                                                  libpq_gettext("out of memory\n"));
                return NULL;
        }
-       memcpy(options, PQconninfoOptions, sizeof(PQconninfoOptions));
+       opt_dest = options;
+
+       for (cur_opt = PQconninfoOptions; cur_opt->keyword; cur_opt++)
+       {
+               /* Only copy the public part of the struct, not the full internal */
+               memcpy(opt_dest, cur_opt, sizeof(PQconninfoOption));
+               opt_dest++;
+       }
+       MemSet(opt_dest, 0, sizeof(PQconninfoOption));
 
        return options;
 }
@@ -5017,6 +5019,20 @@ conninfo_storeval(PQconninfoOption *connOptions,
        PQconninfoOption *option;
        char       *value_copy;
 
+       /*
+        * For backwards compatibility, requiressl=1 gets translated to
+        * sslmode=require, and requiressl=0 gets translated to sslmode=prefer
+        * (which is the default for sslmode).
+        */
+       if (strcmp(keyword, "requiressl") == 0)
+       {
+               keyword = "sslmode";
+               if (value[0] == '1')
+                       value = "require";
+               else
+                       value = "prefer";
+       }
+
        option = conninfo_find(connOptions, keyword);
        if (option == NULL)
        {
@@ -5075,6 +5091,50 @@ conninfo_find(PQconninfoOption *connOptions, const char *keyword)
 }
 
 
+/*
+ * Return the connection options used for the connection
+ */
+PQconninfoOption *
+PQconninfo(PGconn *conn)
+{
+       PQExpBufferData errorBuf;
+       PQconninfoOption *connOptions;
+
+       if (conn == NULL)
+               return NULL;
+
+       /* We don't actually report any errors here, but callees want a buffer */
+       initPQExpBuffer(&errorBuf);
+       if (PQExpBufferDataBroken(errorBuf))
+               return NULL;                    /* out of memory already :-( */
+
+       connOptions = conninfo_init(&errorBuf);
+
+       if (connOptions != NULL)
+       {
+               const internalPQconninfoOption *option;
+
+               for (option = PQconninfoOptions; option->keyword; option++)
+               {
+                       char      **connmember;
+
+                       if (option->connofs < 0)
+                               continue;
+
+                       connmember = (char **) ((char *) conn + option->connofs);
+
+                       if (*connmember)
+                               conninfo_storeval(connOptions, option->keyword, *connmember,
+                                                                 &errorBuf, true, false);
+               }
+       }
+
+       termPQExpBuffer(&errorBuf);
+
+       return connOptions;
+}
+
+
 void
 PQconninfoFree(PQconninfoOption *connOptions)
 {
index 0b8d9a6813c0b91098e13d20b067a47b10a29654..a289ef30907d26048831f54391e5aae6c0de352a 100644 (file)
@@ -262,6 +262,9 @@ extern PQconninfoOption *PQconndefaults(void);
 /* parse connection options in same way as PQconnectdb */
 extern PQconninfoOption *PQconninfoParse(const char *conninfo, char **errmsg);
 
+/* return the connection options used by a live connection */
+extern PQconninfoOption *PQconninfo(PGconn *conn);
+
 /* free the data structure returned by PQconndefaults() or PQconninfoParse() */
 extern void PQconninfoFree(PQconninfoOption *connOptions);