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

Reply via email to