diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 255c5c1..db46cf1 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -496,6 +496,79 @@ typedef struct
+
+ PQconninfoPQconninfo>>
+
+
+ Returns the connection options used by a live connection.
+
+PQconninfoOption *PQconninfo(PGconn *conn, int flags);
+
+
+
+
+ Returns a connection options array. This can be used to determine
+ all possible PQconnectdb options and the
+ values that were used to connect to the server. The return
+ value points to an array of PQconninfoOption
+ structures, which ends with an entry having a null keyword>
+ pointer. All notes above for PQconndefaults also
+ apply to the result of PQconninfo.
+
+
+
+ The parameter flags is used to filter which
+ connection options are returned. The flags are a bitmask that can be
+ ORed together to return parameters of multiple types at the same time.
+
+
+
+ PG_CONNINFO_NORMAL
+
+
+ All normal connection options.
+
+
+
+
+
+ PG_CONNINFO_PASSWORD
+
+
+ All connection options that include passwords or other
+ sensitive information.
+
+
+
+
+
+ PG_CONNINFO_REPLICATION
+
+
+ All connection options only used for replication
+ connections.
+
+
+
+
+
+ PG_CONNINFO_INTERNAL
+
+
+ Internal or backwards compatible connection options.
+ Should not be used by regular applications.
+
+
+
+
+
+ It is also possible to specify PG_CONNINFO_ALL,
+ which will return all options, regardless of type.
+
+
+
+
+
PQconninfoParsePQconninfoParse>>
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index 56d0bb8..93da50d 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -164,3 +164,4 @@ PQsetSingleRowMode 161
lo_lseek64 162
lo_tell64 163
lo_truncate64 164
+PQconninfo 165
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 9eaf410..7f498bb 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -137,81 +137,113 @@ 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 */
+ unsigned int flags; /* type of option */
+} 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, PG_CONNINFO_INTERNAL},
{"service", "PGSERVICE", NULL, NULL,
- "Database-Service", "", 20},
+ "Database-Service", "", 20, -1, PG_CONNINFO_NORMAL},
{"user", "PGUSER", NULL, NULL,
- "Database-User", "", 20},
+ "Database-User", "", 20,
+ offsetof(struct pg_conn, pguser), PG_CONNINFO_NORMAL},
{"password", "PGPASSWORD", NULL, NULL,
- "Database-Password", "*", 20},
+ "Database-Password", "*", 20,
+ offsetof(struct pg_conn, pgpass), PG_CONNINFO_PASSWORD},
{"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), PG_CONNINFO_NORMAL},
{"dbname", "PGDATABASE", NULL, NULL,
- "Database-Name", "", 20},
+ "Database-Name", "", 20,
+ offsetof(struct pg_conn, dbName), PG_CONNINFO_NORMAL},
{"host", "PGHOST", NULL, NULL,
- "Database-Host", "", 40},
+ "Database-Host", "", 40,
+ offsetof(struct pg_conn, pghost), PG_CONNINFO_NORMAL},
{"hostaddr", "PGHOSTADDR", NULL, NULL,
- "Database-Host-IP-Address", "", 45},
+ "Database-Host-IP-Address", "", 45,
+ offsetof(struct pg_conn, pghostaddr), PG_CONNINFO_NORMAL},
{"port", "PGPORT", DEF_PGPORT_STR, NULL,
- "Database-Port", "", 6},
+ "Database-Port", "", 6,
+ offsetof(struct pg_conn, pgport), PG_CONNINFO_NORMAL},
{"client_encoding", "PGCLIENTENCODING", NULL, NULL,
- "Client-Encoding", "", 10},
+ "Client-Encoding", "", 10,
+ offsetof(struct pg_conn, client_encoding_initial), PG_CONNINFO_NORMAL},
/*
* "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), PG_CONNINFO_NORMAL},
{"options", "PGOPTIONS", DefaultOption, NULL,
- "Backend-Debug-Options", "D", 40},
+ "Backend-Debug-Options", "D", 40,
+ offsetof(struct pg_conn, pgoptions), PG_CONNINFO_NORMAL},
{"application_name", "PGAPPNAME", NULL, NULL,
- "Application-Name", "", 64},
+ "Application-Name", "", 64,
+ offsetof(struct pg_conn, appname), PG_CONNINFO_NORMAL},
{"fallback_application_name", NULL, NULL, NULL,
- "Fallback-Application-Name", "", 64},
+ "Fallback-Application-Name", "", 64,
+ offsetof(struct pg_conn, fbappname), PG_CONNINFO_NORMAL},
{"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), PG_CONNINFO_NORMAL},
{"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), PG_CONNINFO_NORMAL},
{"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), PG_CONNINFO_NORMAL},
{"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), PG_CONNINFO_NORMAL},
/*
* ssl options are allowed even without client SSL support because the
@@ -220,30 +252,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), PG_CONNINFO_NORMAL},
{"sslcompression", "PGSSLCOMPRESSION", "1", NULL,
- "SSL-Compression", "", 1},
+ "SSL-Compression", "", 1,
+ offsetof(struct pg_conn, sslcompression), PG_CONNINFO_NORMAL},
{"sslcert", "PGSSLCERT", NULL, NULL,
- "SSL-Client-Cert", "", 64},
+ "SSL-Client-Cert", "", 64,
+ offsetof(struct pg_conn, sslcert), PG_CONNINFO_NORMAL},
{"sslkey", "PGSSLKEY", NULL, NULL,
- "SSL-Client-Key", "", 64},
+ "SSL-Client-Key", "", 64,
+ offsetof(struct pg_conn, sslkey), PG_CONNINFO_NORMAL},
{"sslrootcert", "PGSSLROOTCERT", NULL, NULL,
- "SSL-Root-Certificate", "", 64},
+ "SSL-Root-Certificate", "", 64,
+ offsetof(struct pg_conn, sslrootcert), PG_CONNINFO_NORMAL},
{"sslcrl", "PGSSLCRL", NULL, NULL,
- "SSL-Revocation-List", "", 64},
+ "SSL-Revocation-List", "", 64,
+ offsetof(struct pg_conn, sslcrl), PG_CONNINFO_NORMAL},
{"requirepeer", "PGREQUIREPEER", NULL, NULL,
- "Require-Peer", "", 10},
+ "Require-Peer", "", 10,
+ offsetof(struct pg_conn, requirepeer), PG_CONNINFO_NORMAL},
#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), PG_CONNINFO_NORMAL},
#endif
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
@@ -253,11 +293,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), PG_CONNINFO_NORMAL},
#endif
{"replication", NULL, NULL, NULL,
- "Replication", "D", 5},
+ "Replication", "D", 5,
+ offsetof(struct pg_conn, replication), PG_CONNINFO_REPLICATION},
/* Terminating entry --- MUST BE LAST */
{NULL, NULL, NULL, NULL,
@@ -295,7 +337,8 @@ static PGconn *makeEmptyPGconn(void);
static void fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn);
-static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage);
+static PQconninfoOption *conninfo_init(PQExpBuffer errorMessage,
+ int flags);
static PQconninfoOption *parse_connection_string(const char *conninfo,
PQExpBuffer errorMessage, bool use_defaults);
static int uri_prefix_length(const char *connstr);
@@ -627,7 +670,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 +680,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;
}
/*
@@ -884,7 +874,7 @@ PQconndefaults(void)
if (PQExpBufferDataBroken(errorBuf))
return NULL; /* out of memory already :-( */
- connOptions = conninfo_init(&errorBuf);
+ connOptions = conninfo_init(&errorBuf, PG_CONNINFO_ALL);
if (connOptions != NULL)
{
if (!conninfo_add_defaults(connOptions, &errorBuf))
@@ -4008,18 +3998,35 @@ PQconninfoParse(const char *conninfo, char **errmsg)
* Build a working copy of the constant PQconninfoOptions array.
*/
static PQconninfoOption *
-conninfo_init(PQExpBuffer errorMessage)
+conninfo_init(PQExpBuffer errorMessage, int flags)
{
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++)
+ {
+ if (!(cur_opt->flags & flags))
+ continue;
+
+ /* 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;
}
@@ -4095,7 +4102,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage,
PQconninfoOption *options;
/* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
+ options = conninfo_init(errorMessage, PG_CONNINFO_ALL);
if (options == NULL)
return NULL;
@@ -4295,7 +4302,7 @@ conninfo_array_parse(const char *const * keywords, const char *const * values,
}
/* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
+ options = conninfo_init(errorMessage, PG_CONNINFO_ALL);
if (options == NULL)
{
PQconninfoFree(dbname_options);
@@ -4485,7 +4492,7 @@ conninfo_uri_parse(const char *uri, PQExpBuffer errorMessage,
PQconninfoOption *options;
/* Make a working copy of PQconninfoOptions */
- options = conninfo_init(errorMessage);
+ options = conninfo_init(errorMessage, PG_CONNINFO_ALL);
if (options == NULL)
return NULL;
@@ -5008,6 +5015,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)
{
@@ -5066,6 +5087,50 @@ conninfo_find(PQconninfoOption *connOptions, const char *keyword)
}
+/*
+ * Return the connection options used for the connections
+ */
+PQconninfoOption *
+PQconninfo(PGconn *conn, unsigned int flags)
+{
+ 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, flags);
+
+ 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)
{
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 0b8d9a6..911fd64 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -36,6 +36,15 @@ extern "C"
#define PG_COPYRES_EVENTS 0x04
#define PG_COPYRES_NOTICEHOOKS 0x08
+/*
+ * Option flags for PQconninfo
+ */
+#define PG_CONNINFO_NORMAL 0x01
+#define PG_CONNINFO_PASSWORD 0x02
+#define PG_CONNINFO_REPLICATION 0x04
+#define PG_CONNINFO_INTERNAL 0x80000000
+#define PG_CONNINFO_ALL 0xFFFFFFFF
+
/* Application-visible enum types */
/*
@@ -262,6 +271,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, unsigned int flags);
+
/* free the data structure returned by PQconndefaults() or PQconninfoParse() */
extern void PQconninfoFree(PQconninfoOption *connOptions);