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);