On 20.02.2013 11:42, Amit Kapila wrote:
The patch for providing connection string for pg_basebackup, pg_receivexlog,
pg_dump and pg_dumpall is attached with this mail.

Thanks. Now that I look at this patch, I realize that we don't actually need these new functions for pg_basebackup and friends after all. We already have PQconninfoParse(), we can just use that.

pg_dump can already take a connection string:

pg_dump "dbname=postgres port=5432"

For consistency with psql and other tools, perhaps we should add a "-d" option to pg_dump, so that you could do:

pg_dump -d "dbname=postgres port=5432"

It'd be nice to call the option -d or --dbname in all the tools. That's a bit confusing for pg_basebackup and pg_receivexlog, as it can *not* actually be a database name, but it would be otherwise consistent with the other commands.


I came up with the attached three patches. The first adds -d/--dbname option to pg_basebackup and pg_receivexlog. The second adds it to pg_dump, per above. The third adds it to pg_dumpall.

The third patch is a bit complicated. It first parses the user-specified connection string using PQconninfoParse, so that it can merge in some extra keywords: user, host, password, dbname and fallback_application_name. It then calls PQconnectdbParams with the keyword/value pairs. After making the initial connection to postgres or template1 database, it calls PQconninfo() to again extract the keyword/value pairs in effect in the connection, and constructs a new connection string from them. That new connection string is then passed to pg_dump on the command line, with the database name appended to it.

That seems to work, although it's perhaps a bit Rube Goldbergian. One step of deparsing and parsing could be avoided by keeping the keyword/value pairs from the first PQconninfoParse() call, instead of constructing them again with PQconninfo(). I'll experiment with that tomorrow.


The docs need some improvement. In those commands where you can't pass a database name to the -d/--dbname option, only a connection string, I kept your wording in the docs. But it ought to explain the seemingly strange name for the option, and more. I'll take another whack at that tomorrow as well.


Where does this leave the PQconninfoParseParams/PQconninfodefaultsMerge patch? I'm not sure. Somehow I thought it would be necessary for this work, but it wasn't. I didn't remember that we already have PQconninfoParse() function, which was enough. So, what's the use case for those functions?

- Heikki
>From ebfcff54ae934b38f3bfecfbe8dbe0cbe0573c95 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Wed, 20 Feb 2013 20:49:24 +0200
Subject: [PATCH 1/3] Add -d option, for specifying a connection string, to
 pg_basebackup and pg_receivexlog.

It's a bit strange that the option is called -d/--dbname, when in fact
you can *not* pass a database name to it. But it's consistent with other
client tools, where you can pass a connection string using the -d option.
---
 doc/src/sgml/ref/pg_basebackup.sgml    |   12 +++++
 doc/src/sgml/ref/pg_receivexlog.sgml   |   12 +++++
 src/bin/pg_basebackup/pg_basebackup.c  |    7 ++-
 src/bin/pg_basebackup/pg_receivexlog.c |    7 ++-
 src/bin/pg_basebackup/streamutil.c     |   87 +++++++++++++++++++++++---------
 src/bin/pg_basebackup/streamutil.h     |    1 +
 6 files changed, 101 insertions(+), 25 deletions(-)

diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index 2f89f2c..3ab460a 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -359,6 +359,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 aconflicting options, the user supplied option is used.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-h <replaceable class="parameter">host</replaceable></option></term>
       <term><option>--host=<replaceable class="parameter">host</replaceable></option></term>
       <listitem>
diff --git a/doc/src/sgml/ref/pg_receivexlog.sgml b/doc/src/sgml/ref/pg_receivexlog.sgml
index d06dd1f..314da0e 100644
--- a/doc/src/sgml/ref/pg_receivexlog.sgml
+++ b/doc/src/sgml/ref/pg_receivexlog.sgml
@@ -123,6 +123,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 class="parameter">host</replaceable></option></term>
       <term><option>--host=<replaceable class="parameter">host</replaceable></option></term>
       <listitem>
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index fb5a1bd..2de03ac 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -126,6 +126,7 @@ usage(void)
 	printf(_("  -V, --version          output version information, then exit\n"));
 	printf(_("  -?, --help             show this help, then exit\n"));
 	printf(_("\nConnection options:\n"));
+	printf(_("  -d, --dbname=CONNSTR   connection string\n"));
 	printf(_("  -h, --host=HOSTNAME    database server host or socket directory\n"));
 	printf(_("  -p, --port=PORT        database server port number\n"));
 	printf(_("  -s, --status-interval=INTERVAL\n"
@@ -1540,6 +1541,7 @@ main(int argc, char **argv)
 		{"gzip", no_argument, NULL, 'z'},
 		{"compress", required_argument, NULL, 'Z'},
 		{"label", required_argument, NULL, 'l'},
+		{"dbname", required_argument, NULL, 'd'},
 		{"host", required_argument, NULL, 'h'},
 		{"port", required_argument, NULL, 'p'},
 		{"username", required_argument, NULL, 'U'},
@@ -1572,7 +1574,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "D:F:RxX:l:zZ:c:h:p:U:s:wWvP",
+	while ((c = getopt_long(argc, argv, "D:F:RxX:l:zZ:d:c:h:p:U:s:wWvP",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -1663,6 +1665,9 @@ main(int argc, char **argv)
 					exit(1);
 				}
 				break;
+			case 'd':
+				connection_string = pg_strdup(optarg);
+				break;
 			case 'h':
 				dbhost = pg_strdup(optarg);
 				break;
diff --git a/src/bin/pg_basebackup/pg_receivexlog.c b/src/bin/pg_basebackup/pg_receivexlog.c
index 33dbc50..352ff35 100644
--- a/src/bin/pg_basebackup/pg_receivexlog.c
+++ b/src/bin/pg_basebackup/pg_receivexlog.c
@@ -58,6 +58,7 @@ usage(void)
 	printf(_("  -V, --version          output version information, then exit\n"));
 	printf(_("  -?, --help             show this help, then exit\n"));
 	printf(_("\nConnection options:\n"));
+	printf(_("  -d, --dbname=CONNSTR   connection string\n"));
 	printf(_("  -h, --host=HOSTNAME    database server host or socket directory\n"));
 	printf(_("  -p, --port=PORT        database server port number\n"));
 	printf(_("  -s, --status-interval=INTERVAL\n"
@@ -306,6 +307,7 @@ main(int argc, char **argv)
 		{"help", no_argument, NULL, '?'},
 		{"version", no_argument, NULL, 'V'},
 		{"directory", required_argument, NULL, 'D'},
+		{"dbname", required_argument, NULL, 'd'},
 		{"host", required_argument, NULL, 'h'},
 		{"port", required_argument, NULL, 'p'},
 		{"username", required_argument, NULL, 'U'},
@@ -338,7 +340,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "D:h:p:U:s:nwWv",
+	while ((c = getopt_long(argc, argv, "D:d:h:p:U:s:nwWv",
 							long_options, &option_index)) != -1)
 	{
 		switch (c)
@@ -346,6 +348,9 @@ main(int argc, char **argv)
 			case 'D':
 				basedir = pg_strdup(optarg);
 				break;
+			case 'd':
+				connection_string = pg_strdup(optarg);
+				break;
 			case 'h':
 				dbhost = pg_strdup(optarg);
 				break;
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 8a43c4b..a878dd4 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -18,6 +18,7 @@
 #include <string.h>
 
 const char *progname;
+char	   *connection_string = NULL;
 char	   *dbhost = NULL;
 char	   *dbuser = NULL;
 char	   *dbport = NULL;
@@ -34,31 +35,67 @@ PGconn *
 GetConnection(void)
 {
 	PGconn	   *tmpconn;
-	int			argcount = 4;	/* dbname, replication, fallback_app_name,
-								 * password */
+	int			argcount = 7;	/* dbname, replication, fallback_app_name,
+								 * host, user, port, password */
 	int			i;
 	const char **keywords;
 	const char **values;
 	char	   *password = NULL;
 	const char *tmpparam;
+	PQconninfoOption *conn_opts = NULL;
+	PQconninfoOption *conn_opt;
+	char	   *err_msg = NULL;
+
+	/*
+	 * Merge the connection info inputs given in form of connection string,
+	 * options and default values (dbname=replication, replication=true,
+	 * etc.)
+	 */
+	i = 0;
+	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] = "dbname";
+	values[i] = "replication";
+	i++;
+	keywords[i] = "replication";
+	values[i] = "true";
+	i++;
+	keywords[i] = "fallback_application_name";
+	values[i] = progname;
+	i++;
 
-	if (dbhost)
-		argcount++;
-	if (dbuser)
-		argcount++;
-	if (dbport)
-		argcount++;
-
-	keywords = pg_malloc0((argcount + 1) * sizeof(*keywords));
-	values = pg_malloc0((argcount + 1) * sizeof(*values));
-
-	keywords[0] = "dbname";
-	values[0] = "replication";
-	keywords[1] = "replication";
-	values[1] = "true";
-	keywords[2] = "fallback_application_name";
-	values[2] = progname;
-	i = 3;
 	if (dbhost)
 	{
 		keywords[i] = "host";
@@ -90,15 +127,15 @@ GetConnection(void)
 			 * meaning this is the call for a second session to the same
 			 * database, so just forcibly reuse that password.
 			 */
-			keywords[argcount - 1] = "password";
-			values[argcount - 1] = dbpassword;
+			keywords[i] = "password";
+			values[i] = dbpassword;
 			dbgetpassword = -1; /* Don't try again if this fails */
 		}
 		else if (dbgetpassword == 1)
 		{
 			password = simple_prompt(_("Password: "), 100, false);
-			keywords[argcount - 1] = "password";
-			values[argcount - 1] = password;
+			keywords[i] = "password";
+			values[i] = password;
 		}
 
 		tmpconn = PQconnectdbParams(keywords, values, true);
@@ -130,12 +167,16 @@ GetConnection(void)
 			PQfinish(tmpconn);
 			free(values);
 			free(keywords);
+			if (conn_opts)
+				PQconninfoFree(conn_opts);
 			return NULL;
 		}
 
 		/* Connection ok! */
 		free(values);
 		free(keywords);
+		if (conn_opts)
+			PQconninfoFree(conn_opts);
 
 		/*
 		 * Ensure we have the same value of integer timestamps as the server
diff --git a/src/bin/pg_basebackup/streamutil.h b/src/bin/pg_basebackup/streamutil.h
index 4f5ff91..77d6b86 100644
--- a/src/bin/pg_basebackup/streamutil.h
+++ b/src/bin/pg_basebackup/streamutil.h
@@ -1,6 +1,7 @@
 #include "libpq-fe.h"
 
 extern const char *progname;
+extern char *connection_string;
 extern char *dbhost;
 extern char *dbuser;
 extern char *dbport;
-- 
1.7.10.4

>From 4a5633852134dd7bf8687f72f6fa6eaa00059952 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@iki.fi>
Date: Wed, 20 Feb 2013 20:50:59 +0200
Subject: [PATCH 2/3] Add -d option to pg_dump.

You could already pass a database name on the command line, as the last
argument without any option character. But for consistency with psql and
other client tools, also accept it using -d/--dbname.
---
 doc/src/sgml/ref/pg_dump.sgml |   20 ++++++++++++++++++++
 src/bin/pg_dump/pg_dump.c     |   15 ++++++++++++---
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 152edcb..6d0f214 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -818,6 +818,26 @@ PostgreSQL documentation
 
     <variablelist>
      <varlistentry>
+      <term><option>-d <replaceable class="parameter">dbname</replaceable></></term>
+      <term><option>--dbname=<replaceable class="parameter">dbname</replaceable></></term>
+      <listitem>
+      <para>
+       Specifies the name of the database to connect to. This is
+       equivalent to specifying <replaceable
+       class="parameter">dbname</replaceable> as the first non-option
+       argument on the command line.
+      </para>
+      <para>
+       If this parameter contains an <symbol>=</symbol> sign or starts
+       with a valid <acronym>URI</acronym> prefix
+       (<literal>postgresql://</literal>
+       or <literal>postgres://</literal>), it is treated as a
+       <parameter>conninfo</parameter> string. See <xref linkend="libpq-connect"> for more information.
+      </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-h <replaceable class="parameter">host</replaceable></option></term>
       <term><option>--host=<replaceable class="parameter">host</replaceable></option></term>
       <listitem>
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 43d571c..4923847 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -307,6 +307,7 @@ main(int argc, char **argv)
 		{"blobs", no_argument, NULL, 'b'},
 		{"clean", no_argument, NULL, 'c'},
 		{"create", no_argument, NULL, 'C'},
+		{"dbname", required_argument, NULL, 'd'},
 		{"file", required_argument, NULL, 'f'},
 		{"format", required_argument, NULL, 'F'},
 		{"host", required_argument, NULL, 'h'},
@@ -387,7 +388,7 @@ main(int argc, char **argv)
 		}
 	}
 
-	while ((c = getopt_long(argc, argv, "abcCE:f:F:h:in:N:oOp:RsS:t:T:U:vwWxZ:",
+	while ((c = getopt_long(argc, argv, "abcCd:E:f:F:h:iK:n:N:oOp:RsS:t:T:U:vwWxZ:",
 							long_options, &optindex)) != -1)
 	{
 		switch (c)
@@ -408,6 +409,10 @@ main(int argc, char **argv)
 				outputCreateDB = 1;
 				break;
 
+			case 'd':			/* database name */
+				dbname = pg_strdup(optarg);
+				break;
+
 			case 'E':			/* Dump encoding */
 				dumpencoding = pg_strdup(optarg);
 				break;
@@ -520,8 +525,11 @@ main(int argc, char **argv)
 		}
 	}
 
-	/* Get database name from command line */
-	if (optind < argc)
+	/*
+	 * Non-option argument specifies database name as long as it wasn't
+	 * already specified with -d / --dbname
+	 */
+	if (optind < argc && dbname == NULL)
 		dbname = argv[optind++];
 
 	/* Complain if any arguments remain */
@@ -872,6 +880,7 @@ help(const char *progname)
 			 "                               ALTER OWNER commands to set ownership\n"));
 
 	printf(_("\nConnection options:\n"));
+	printf(_("  -d, --dbname=DBNAME      database name to connect to\n"));
 	printf(_("  -h, --host=HOSTNAME      database server host or socket directory\n"));
 	printf(_("  -p, --port=PORT          database server port number\n"));
 	printf(_("  -U, --username=NAME      connect as specified database user\n"));
-- 
1.7.10.4

>From 2586b7e0edbed46ba7fe97809e19fc53ac815e19 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakan...@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

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