>From 2586b7e0edbed46ba7fe97809e19fc53ac815e19 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 20 Feb 2013 20:56:17 +0200
Subject: [PATCH 3/3] Add -d option to pg_dumpall, for specifying a connection
 string.

Like with pg_basebackup and pg_receivexlog, it's a bit strange to call the
option -d/--dbname, when in fact you cannot pass a database name in it
(XXX: would it make sense to allow passing the "initial" database in it,
instead of -l). But that's what the option to pass a connection string is
called in other client tools, so seems good to be consistent.
---
 doc/src/sgml/ref/pg_dumpall.sgml |   12 +++
 src/bin/pg_dump/pg_dumpall.c     |  194 +++++++++++++++++++++++++++++---------
 2 files changed, 164 insertions(+), 42 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 253ee01..e57d597 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -406,6 +406,18 @@ PostgreSQL documentation
 
    <variablelist>
      <varlistentry>
+      <term><option>-d <replaceable class="parameter">connstr</replaceable></option></term>
+      <term><option>--dbname=<replaceable class="parameter">connstr</replaceable></option></term>
+      <listitem>
+       <para>
+        Specifies connection string options, used for connecting to server.
+        These options can be used along with other user supplied options.
+        In case of conflicting options, the user supplied option is used.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-h <replaceable>host</replaceable></option></term>
       <term><option>--host=<replaceable>host</replaceable></option></term>
       <listitem>
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index a6e6a0f..84280f8 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -56,13 +56,14 @@ static int	runPgDump(const char *dbname);
 static void buildShSecLabels(PGconn *conn, const char *catalog_name,
 				 uint32 objectId, PQExpBuffer buffer,
 				 const char *target, const char *objname);
-static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
+static PGconn *connectDatabase(const char *dbname, const char *connstr, const char *pghost, const char *pgport,
 	  const char *pguser, enum trivalue prompt_password, bool fail_on_error);
 static PGresult *executeQuery(PGconn *conn, const char *query);
 static void executeCommand(PGconn *conn, const char *query);
 
 static char pg_dump_bin[MAXPGPATH];
 static PQExpBuffer pgdumpopts;
+static char *connstr = "";
 static bool skip_acls = false;
 static bool verbose = false;
 
@@ -91,6 +92,7 @@ main(int argc, char *argv[])
 		{"globals-only", no_argument, NULL, 'g'},
 		{"host", required_argument, NULL, 'h'},
 		{"ignore-version", no_argument, NULL, 'i'},
+		{"dbname", required_argument, NULL, 'd'},
 		{"database", required_argument, NULL, 'l'},
 		{"oids", no_argument, NULL, 'o'},
 		{"no-owner", no_argument, NULL, 'O'},
@@ -188,7 +190,7 @@ main(int argc, char *argv[])
 
 	pgdumpopts = createPQExpBuffer();
 
-	while ((c = getopt_long(argc, argv, "acf:gh:il:oOp:rsS:tU:vwWx", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "acd:f:gh:i:l:oOp:rsS:tU:vwWx", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -201,6 +203,10 @@ main(int argc, char *argv[])
 				output_clean = true;
 				break;
 
+			case 'd':
+				connstr = pg_strdup(optarg);
+				break;
+
 			case 'f':
 				filename = pg_strdup(optarg);
 				appendPQExpBuffer(pgdumpopts, " -f ");
@@ -213,8 +219,6 @@ main(int argc, char *argv[])
 
 			case 'h':
 				pghost = pg_strdup(optarg);
-				appendPQExpBuffer(pgdumpopts, " -h ");
-				doShellQuoting(pgdumpopts, pghost);
 				break;
 
 			case 'i':
@@ -235,8 +239,6 @@ main(int argc, char *argv[])
 
 			case 'p':
 				pgport = pg_strdup(optarg);
-				appendPQExpBuffer(pgdumpopts, " -p ");
-				doShellQuoting(pgdumpopts, pgport);
 				break;
 
 			case 'r':
@@ -258,8 +260,6 @@ main(int argc, char *argv[])
 
 			case 'U':
 				pguser = pg_strdup(optarg);
-				appendPQExpBuffer(pgdumpopts, " -U ");
-				doShellQuoting(pgdumpopts, pguser);
 				break;
 
 			case 'v':
@@ -370,7 +370,7 @@ main(int argc, char *argv[])
 	 */
 	if (pgdb)
 	{
-		conn = connectDatabase(pgdb, pghost, pgport, pguser,
+		conn = connectDatabase(pgdb, connstr, pghost, pgport, pguser,
 							   prompt_password, false);
 
 		if (!conn)
@@ -382,10 +382,10 @@ main(int argc, char *argv[])
 	}
 	else
 	{
-		conn = connectDatabase("postgres", pghost, pgport, pguser,
+		conn = connectDatabase("postgres", connstr, pghost, pgport, pguser,
 							   prompt_password, false);
 		if (!conn)
-			conn = connectDatabase("template1", pghost, pgport, pguser,
+			conn = connectDatabase("template1", connstr, pghost, pgport, pguser,
 								   prompt_password, true);
 
 		if (!conn)
@@ -400,6 +400,48 @@ main(int argc, char *argv[])
 	}
 
 	/*
+	 * Now that we're connected, build the stem of the connection string that
+	 * we're going to pass to pg_dump.
+	 */
+	{
+		PQconninfoOption *conn_opts;
+		PQconninfoOption *conn_opt;
+		PQExpBuffer buf;
+
+		/* Extract the effective options used for the connection */
+		conn_opts = PQconninfo(conn);
+		if (!conn_opts)
+		{
+			fprintf(stderr, _("%s: out of memory\n"), progname);
+			exit_nicely(1);
+		}
+
+		/*
+		 * Construct a new connection string in key/value format, from the
+		 * options used in the current connection. 'dbname' option is left
+		 * out, because that varies in each pg_dump invocation.
+		 */
+		buf = createPQExpBuffer();
+		for (conn_opt = conn_opts; conn_opt->keyword; conn_opt++)
+		{
+			if (conn_opt->val == NULL || *conn_opt->val == '\0')
+				continue;
+
+			if (strcmp(conn_opt->keyword, "dbname") == 0)
+				continue;
+
+			appendPQExpBuffer(buf, "%s=", conn_opt->keyword);
+			doConnStrQuoting(buf, conn_opt->val);
+			appendPQExpBuffer(buf, " ");
+		}
+		connstr = pg_strdup(buf->data);
+		destroyPQExpBuffer(buf);
+
+		fprintf(stderr, "connstr: %s\n", connstr);
+
+	}
+
+	/*
 	 * Open the output file if required, otherwise use stdout
 	 */
 	if (filename)
@@ -568,6 +610,7 @@ help(void)
 			 "                               ALTER OWNER commands to set ownership\n"));
 
 	printf(_("\nConnection options:\n"));
+	printf(_("  -d, --dbname=CONNSTR     connect to server using connection string\n"));
 	printf(_("  -h, --host=HOSTNAME      database server host or socket directory\n"));
 	printf(_("  -l, --database=DBNAME    alternative default database\n"));
 	printf(_("  -p, --port=PORT          database server port number\n"));
@@ -1630,7 +1673,7 @@ dumpDatabases(PGconn *conn)
 static int
 runPgDump(const char *dbname)
 {
-	PQExpBuffer connstr = createPQExpBuffer();
+	PQExpBuffer connstrbuf = createPQExpBuffer();
 	PQExpBuffer cmd = createPQExpBuffer();
 	int			ret;
 
@@ -1652,11 +1695,10 @@ runPgDump(const char *dbname)
 	 * database name as is, but if it contains any = characters, it would
 	 * incorrectly treat it as a connection string.
 	 */
-	appendPQExpBuffer(connstr, "dbname='");
-	doConnStrQuoting(connstr, dbname);
-	appendPQExpBuffer(connstr, "'");
+	appendPQExpBuffer(connstrbuf, "%sdbname=", connstr);
+	doConnStrQuoting(connstrbuf, dbname);
 
-	doShellQuoting(cmd, connstr->data);
+	doShellQuoting(cmd, connstrbuf->data);
 
 	appendPQExpBuffer(cmd, "%s", SYSTEMQUOTE);
 
@@ -1669,7 +1711,7 @@ runPgDump(const char *dbname)
 	ret = system(cmd->data);
 
 	destroyPQExpBuffer(cmd);
-	destroyPQExpBuffer(connstr);
+	destroyPQExpBuffer(connstrbuf);
 
 	return ret;
 }
@@ -1705,7 +1747,7 @@ buildShSecLabels(PGconn *conn, const char *catalog_name, uint32 objectId,
  * on failure, but preserve any prompted password for the next try.
  */
 static PGconn *
-connectDatabase(const char *dbname, const char *pghost, const char *pgport,
+connectDatabase(const char *dbname, const char *connection_string, const char *pghost, const char *pgport,
 	   const char *pguser, enum trivalue prompt_password, bool fail_on_error)
 {
 	PGconn	   *conn;
@@ -1723,30 +1765,77 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
 	 */
 	do
 	{
-#define PARAMS_ARRAY_SIZE	7
-		const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
-		const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
-
-		keywords[0] = "host";
-		values[0] = pghost;
-		keywords[1] = "port";
-		values[1] = pgport;
-		keywords[2] = "user";
-		values[2] = pguser;
-		keywords[3] = "password";
-		values[3] = password;
-		keywords[4] = "dbname";
-		values[4] = dbname;
-		keywords[5] = "fallback_application_name";
-		values[5] = progname;
-		keywords[6] = NULL;
-		values[6] = NULL;
+		int			argcount = 6;
+		const char **keywords;
+		const char **values;
+		PQconninfoOption *conn_opts = NULL;
+		PQconninfoOption *conn_opt;
+		char	   *err_msg = NULL;
+		int			i = 0;
+
+		/*
+		 * Merge the connection info inputs given in form of connection string
+		 * and other options.
+		 */
+		if (connection_string)
+		{
+			conn_opts = PQconninfoParse(connection_string, &err_msg);
+			if (conn_opts == NULL)
+			{
+				fprintf(stderr, "%s: %s\n", progname, err_msg);
+				return NULL;
+			}
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0')
+					argcount++;
+			}
+
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+
+			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
+			{
+				if (conn_opt->val != NULL && conn_opt->val[0] != '\0')
+				{
+					keywords[i] = conn_opt->keyword;
+					values[i] = conn_opt->val;
+					i++;
+				}
+			}
+		}
+		else
+		{
+			keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
+			values = pg_malloc0((argcount + 1) * sizeof(*values));
+		}
+
+		keywords[i] = "host";
+		values[i] = pghost;
+		i++;
+		keywords[i] = "port";
+		values[i] = pgport;
+		i++;
+		keywords[i] = "user";
+		values[i] = pguser;
+		i++;
+		keywords[i] = "password";
+		values[i] = password;
+		i++;
+		keywords[i] = "dbname";
+		values[i] = dbname;
+		i++;
+		keywords[i] = "fallback_application_name";
+		values[i] = progname;
+		i++;
 
 		new_pass = false;
 		conn = PQconnectdbParams(keywords, values, true);
 
 		free(keywords);
 		free(values);
+		PQconninfoFree(conn_opts);
 
 		if (!conn)
 		{
@@ -1917,15 +2006,36 @@ dumpTimestamp(char *msg)
 static void
 doConnStrQuoting(PQExpBuffer buf, const char *str)
 {
-	while (*str)
+	const char *s;
+	bool needquotes;
+
+	needquotes = false;
+	for (s = str; *s; s++)
+	{
+		if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+			  (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+		{
+			needquotes = true;
+			break;
+		}
+	}
+
+	if (needquotes)
 	{
-		/* ' and \ must be escaped by to \' and \\ */
-		if (*str == '\'' || *str == '\\')
-			appendPQExpBufferChar(buf, '\\');
+		appendPQExpBufferChar(buf, '\'');
+		while (*str)
+		{
+			/* ' and \ must be escaped by to \' and \\ */
+			if (*str == '\'' || *str == '\\')
+				appendPQExpBufferChar(buf, '\\');
 
-		appendPQExpBufferChar(buf, *str);
-		str++;
+			appendPQExpBufferChar(buf, *str);
+			str++;
+		}
+		appendPQExpBufferChar(buf, '\'');
 	}
+	else
+		appendPQExpBufferStr(buf, str);
 }
 
 /*
-- 
1.7.10.4

