From 3dee636e0404885d07885d41c0d70e50c784f324 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 25 Feb 2013 19:39:10 +0200 Subject: [PATCH] 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. Original patch by Amit Kapila, heavily modified by me. --- doc/src/sgml/ref/pg_dumpall.sgml | 19 +++ src/bin/pg_dump/pg_dumpall.c | 209 ++++++++++++++++++++++++------- 2 files changed, 184 insertions(+), 44 deletions(-) diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml index 253ee01c0e..5c6a101333 100644 --- a/doc/src/sgml/ref/pg_dumpall.sgml +++ b/doc/src/sgml/ref/pg_dumpall.sgml @@ -405,6 +405,25 @@ PostgreSQL documentation The following command-line options control the database connection parameters. + + + + + + Specifies parameters used to connect to the server, as a connection + string. See for more information. + + + The option is called --dbname for consistency with other + client applications, but because pg_dumpall + needs to connect to many databases, database name in the connection + string will be ignored. Use -l option to specify + the name of the database used to dump global objects and to discover + what other databases should be dumped. + + + + diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index a6e6a0fa9a..ac3f57e5fe 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -56,13 +56,15 @@ 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 char *constructConnStr(const char **keywords, const char **values); 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 +93,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 +191,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 +204,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 +220,6 @@ main(int argc, char *argv[]) case 'h': pghost = pg_strdup(optarg); - appendPQExpBuffer(pgdumpopts, " -h "); - doShellQuoting(pgdumpopts, pghost); break; case 'i': @@ -235,8 +240,6 @@ main(int argc, char *argv[]) case 'p': pgport = pg_strdup(optarg); - appendPQExpBuffer(pgdumpopts, " -p "); - doShellQuoting(pgdumpopts, pgport); break; case 'r': @@ -258,8 +261,6 @@ main(int argc, char *argv[]) case 'U': pguser = pg_strdup(optarg); - appendPQExpBuffer(pgdumpopts, " -U "); - doShellQuoting(pgdumpopts, pguser); break; case 'v': @@ -370,7 +371,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 +383,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) @@ -568,6 +569,7 @@ help(void) " ALTER OWNER commands to set ownership\n")); printf(_("\nConnection options:\n")); + printf(_(" -d, --dbname=CONNSTR connect 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 +1632,7 @@ dumpDatabases(PGconn *conn) static int runPgDump(const char *dbname) { - PQExpBuffer connstr = createPQExpBuffer(); + PQExpBuffer connstrbuf = createPQExpBuffer(); PQExpBuffer cmd = createPQExpBuffer(); int ret; @@ -1647,16 +1649,13 @@ runPgDump(const char *dbname) appendPQExpBuffer(cmd, " -Fp "); /* - * Construct a connection string from the database name, like - * dbname=''. pg_dump would usually also accept the - * database name as is, but if it contains any = characters, it would - * incorrectly treat it as a connection string. + * Append the database name to the already-constructed stem of connection + * string. */ - appendPQExpBuffer(connstr, "dbname='"); - doConnStrQuoting(connstr, dbname); - appendPQExpBuffer(connstr, "'"); + appendPQExpBuffer(connstrbuf, "%s dbname=", connstr); + doConnStrQuoting(connstrbuf, dbname); - doShellQuoting(cmd, connstr->data); + doShellQuoting(cmd, connstrbuf->data); appendPQExpBuffer(cmd, "%s", SYSTEMQUOTE); @@ -1669,7 +1668,7 @@ runPgDump(const char *dbname) ret = system(cmd->data); destroyPQExpBuffer(cmd); - destroyPQExpBuffer(connstr); + destroyPQExpBuffer(connstrbuf); return ret; } @@ -1703,16 +1702,23 @@ buildShSecLabels(PGconn *conn, const char *catalog_name, uint32 objectId, * * If fail_on_error is false, we return NULL without printing any message * on failure, but preserve any prompted password for the next try. + * + * On success, the global variable 'connstr' is set to a connection string + * containing the options used. */ static PGconn * -connectDatabase(const char *dbname, const char *pghost, const char *pgport, - const char *pguser, enum trivalue prompt_password, bool fail_on_error) +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; bool new_pass; const char *remoteversion_str; int my_version; static char *password = NULL; + const char **keywords = NULL; + const char **values = NULL; + PQconninfoOption *conn_opts = NULL; if (prompt_password == TRI_YES && !password) password = simple_prompt("Password: ", 100, false); @@ -1723,31 +1729,93 @@ 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; + PQconninfoOption *conn_opt; + char *err_msg = NULL; + int i = 0; + + if (keywords) + free(keywords); + if (values) + free(values); + if (conn_opts) + PQconninfoFree(conn_opts); + + /* + * 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); + exit_nicely(1); + } + + 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)); + } + + if (pghost) + { + keywords[i] = "host"; + values[i] = pghost; + i++; + } + if (pgport) + { + keywords[i] = "port"; + values[i] = pgport; + i++; + } + if (pguser) + { + keywords[i] = "user"; + values[i] = pguser; + i++; + } + if (password) + { + keywords[i] = "password"; + values[i] = password; + i++; + } + if (dbname) + { + 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); - if (!conn) { fprintf(stderr, _("%s: could not connect to database \"%s\"\n"), @@ -1779,10 +1847,26 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport, else { PQfinish(conn); + + free(keywords); + free(values); + PQconninfoFree(conn_opts); + return NULL; } } + /* + * Ok, connected successfully. Remember the options used, in the form of + * a connection string. + */ + connstr = constructConnStr(keywords, values); + + free(keywords); + free(values); + PQconninfoFree(conn_opts); + + /* Check version */ remoteversion_str = PQparameterStatus(conn, "server_version"); if (!remoteversion_str) { @@ -1829,6 +1913,43 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport, return conn; } +/* ---------- + * Construct a connection string from the given keyword/value pairs. It is + * used to pass the connection options to the pg_dump subprocess. + * + * The following parameters are excluded: + * dbname - varies in each pg_dump invocation + * password - it's not secure to pass a password on the command line + * fallback_application_name - we'll let pg_dump set it + * ---------- + */ +static char * +constructConnStr(const char **keywords, const char **values) +{ + PQExpBuffer buf = createPQExpBuffer(); + char *connstr; + int i; + bool firstkeyword = true; + + /* Construct a new connection string in key='value' format. */ + for (i = 0; keywords[i] != NULL; i++) + { + if (strcmp(keywords[i], "dbname") == 0 || + strcmp(keywords[i], "password") == 0 || + strcmp(keywords[i], "fallback_application_name") == 0) + continue; + + if (!firstkeyword) + appendPQExpBufferChar(buf, ' '); + firstkeyword = false; + appendPQExpBuffer(buf, "%s=", keywords[i]); + doConnStrQuoting(buf, values[i]); + } + + connstr = pg_strdup(buf->data); + destroyPQExpBuffer(buf); + return connstr; +} /* * Run a query, return the results, exit program on failure. -- 2.40.0