On Wed, Aug 26, 2015 at 5:44 PM, Pavel Stehule <[email protected]>
wrote:
> Hi
>
> 2015-08-25 17:21 GMT+02:00 Tom Lane <[email protected]>:
>
>> Jim Nasby <[email protected]> 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers