On Wed, Aug 26, 2015 at 5:44 PM, Pavel Stehule <pavel.steh...@gmail.com> wrote:
> Hi > > 2015-08-25 17:21 GMT+02:00 Tom Lane <t...@sss.pgh.pa.us>: > >> Jim Nasby <jim.na...@bluetreble.com> writes: >> > What I've had problems with is trying to correlate psql specified >> > connection attributes with things like DBI. It would be nice if there >> > was a way to get a fully formed connection URI for the current >> connection. >> >> Yeah, although I'd think the capability to create such a URI is libpq's >> province not psql's. Maybe a PQgetConnectionURI(PGConn) function in >> libpq, and some psql backslash command to access that? Or maybe a nicer >> API would be that there's a magic psql variable containing the URI; not >> sure. >> > > proof concept of PQGetConnectionUri and \uri command. > I like the idea, thanks! > missing: > > connection options > uri encoding > Attached adds implementation of both. Still missing: - documentation Maybe we should provide a bool parameter to this new function so that additional parameters could be ignored. Right now it will print a few default values, that are of no great use anyway: $ ./bin/psql -c '\uri' 'postgresql://username@/postgres' postgresql:/username@ :5432/postgres?client_encoding=UTF8&fallback_application_name=psql&sslmode=disable I don't think we can detect and remove the default values from this output in a reliable way? -- Alex
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 6181a61..47e27cd 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1505,6 +1505,26 @@ exec_command(const char *cmd, free(opt); } + /* \uri */ + else if (strcmp(cmd, "uri") == 0) + { + char *db = PQdb(pset.db); + + if (db == NULL) + printf(_("You are currently not connected to a database.\n")); + else + { + char *uri = PQgetConnectionUri(pset.db); + if (uri == NULL) + { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + printf("%s\n", uri); + free(uri); + } + } + /* \w -- write query buffer to file */ else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0) { diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 4a21bf1..c1165c9 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -169,3 +169,4 @@ PQsslInUse 166 PQsslStruct 167 PQsslAttributes 168 PQsslAttribute 169 +PQgetConnectionUri 170 diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a45f4cb..561dee3 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -5361,6 +5361,118 @@ PQport(const PGconn *conn) return conn->pgport; } +#define isdef(strptr) ((strptr) && (strptr[0] != '\0')) + +#define APPEND_URI_FIELD(key, fieldname) \ + \ + if (isdef(conn->fieldname)) \ + { \ + if (qs) \ + appendPQExpBufferChar(&buf, '&'); \ + else \ + appendPQExpBufferChar(&buf, '?'); \ + qs = true; \ + \ + appendPQExpBufferStr(&buf, #key "="); \ + escape_uri(&buf, conn->fieldname); \ + } \ + + +static void +escape_uri(PQExpBuffer buf, const char *str) +{ + static const char hextbl[] = "0123456789ABCDEF"; + const char *p; + + for (p = str; *p; p++) + { + if (*p == '-' || *p == '_' || (isgraph(*p) && !ispunct(*p))) + appendPQExpBufferChar(buf, *p); + else + { + appendPQExpBufferChar(buf, '%'); + appendPQExpBufferChar(buf, hextbl[(*p >> 4) & 0x0F]); + appendPQExpBufferChar(buf, hextbl[*p & 0x0F]); + } + } +} + +/* + * Returns string uri - returned string should be released + */ +char * +PQgetConnectionUri(const PGconn *conn) +{ + PQExpBufferData buf; + const char *host; + bool qs; + + if (!conn) + return NULL; + + host = PQhost(conn); + + initPQExpBuffer(&buf); + + /* build the main uri part */ + appendPQExpBufferStr(&buf, "postgresql://"); + escape_uri(&buf, conn->pguser); + + if (isdef(conn->pgpass)) + { + appendPQExpBufferChar(&buf, ':'); + escape_uri(&buf, conn->pgpass); + } + appendPQExpBufferChar(&buf, '@'); + if (isdef(host)) + escape_uri(&buf, host); + + if (isdef(conn->pgport)) + { + appendPQExpBufferChar(&buf, ':'); + escape_uri(&buf, conn->pgport); + } + if (isdef(conn->dbName)) + { + appendPQExpBufferChar(&buf, '/'); + escape_uri(&buf, conn->dbName); + } + + /* optional query string parameters follow */ + qs = false; + APPEND_URI_FIELD(connect_timeout, connect_timeout); + APPEND_URI_FIELD(client_encoding, client_encoding_initial); + APPEND_URI_FIELD(options, pgoptions); + APPEND_URI_FIELD(application_name, appname); + APPEND_URI_FIELD(fallback_application_name, fbappname); + APPEND_URI_FIELD(keepalives, keepalives); + APPEND_URI_FIELD(keepalives_idle, keepalives_idle); + APPEND_URI_FIELD(keepalives_interval, keepalives_interval); + APPEND_URI_FIELD(keepalives_count, keepalives_count); + APPEND_URI_FIELD(sslmode, sslmode); + if (isdef(conn->sslmode) && strcmp(conn->sslmode, "disable")) + { + APPEND_URI_FIELD(sslcompression, sslcompression); + APPEND_URI_FIELD(sslcert, sslcert); + APPEND_URI_FIELD(sslkey, sslkey); + APPEND_URI_FIELD(sslrootcert, sslrootcert); + APPEND_URI_FIELD(sslcrl, sslcrl); + } + APPEND_URI_FIELD(requirepeer, requirepeer); +#if defined(ENABLE_GSS) || defined(ENABLE_SSPI) + APPEND_URI_FIELD(krbsrvname, krbsrvname); +#endif +#if defined(ENABLE_SSPI) && defined(ENABLE_GSS) + APPEND_URI_FIELD(gsslib, gsslib); +#endif + + return buf.data; +} + +#undef isdef +#undef APPEND_URI_FIELD + + char * PQtty(const PGconn *conn) { diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index a73eae2..7c8a212 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -304,6 +304,7 @@ extern char *PQhost(const PGconn *conn); extern char *PQport(const PGconn *conn); extern char *PQtty(const PGconn *conn); extern char *PQoptions(const PGconn *conn); +extern char *PQgetConnectionUri(const PGconn *conn); extern ConnStatusType PQstatus(const PGconn *conn); extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn); extern const char *PQparameterStatus(const PGconn *conn,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers