prep_status("Adjusting sequences");
exec_prog(true,
SYSTEMQUOTE "\"%s/psql\" --set ON_ERROR_STOP=on "
- "--no-psqlrc --port %d --username \"%s\" "
+ "--no-psqlrc --port %d --username %s "
"-f \"%s\" --dbname template1 >> \"%s\"" SYSTEMQUOTE,
- new_cluster.bindir, new_cluster.port, os_info.user,
+ new_cluster.bindir, new_cluster.port,
+ os_info.user_shell_arg,
sequence_script_file_name, log_opts.filename);
unlink(sequence_script_file_name);
check_ok();
* restores the frozenid's for databases and relations.
*/
exec_prog(true,
- SYSTEMQUOTE "\"%s/pg_dumpall\" --port %d --username \"%s\" "
+ SYSTEMQUOTE "\"%s/pg_dumpall\" --port %d --username %s "
"--schema-only --quote-all-identifiers --binary-upgrade "
"-f \"%s/" ALL_DUMP_FILE "\""
- SYSTEMQUOTE, new_cluster.bindir, old_cluster.port, os_info.user, os_info.cwd);
+ SYSTEMQUOTE, new_cluster.bindir, old_cluster.port,
+ os_info.user_shell_arg, os_info.cwd);
check_ok();
}
int option; /* Command line option */
int optindex = 0; /* used by getopt_long */
int os_user_effective_id;
+ PQExpBufferData userbuf;
user_opts.transfer_mode = TRANSFER_MODE_COPY;
"old cluster data resides");
validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
"new cluster data resides");
+
+ initPQExpBuffer(&userbuf);
+ appendShellString(&userbuf, os_info.user);
+ /* Abandon struct, but keep its buffer until process exit. */
+ os_info.user_shell_arg = userbuf.data;
}
*/
prep_status("Analyzing all rows in the new cluster");
exec_prog(true,
- SYSTEMQUOTE "\"%s/vacuumdb\" --port %d --username \"%s\" "
+ SYSTEMQUOTE "\"%s/vacuumdb\" --port %d --username %s "
"--all --analyze >> \"%s\" 2>&1" SYSTEMQUOTE,
- new_cluster.bindir, new_cluster.port, os_info.user,
+ new_cluster.bindir, new_cluster.port, os_info.user_shell_arg,
#ifndef WIN32
log_opts.filename
#else
*/
prep_status("Freezing all rows on the new cluster");
exec_prog(true,
- SYSTEMQUOTE "\"%s/vacuumdb\" --port %d --username \"%s\" "
+ SYSTEMQUOTE "\"%s/vacuumdb\" --port %d --username %s "
"--all --freeze >> \"%s\" 2>&1" SYSTEMQUOTE,
- new_cluster.bindir, new_cluster.port, os_info.user,
+ new_cluster.bindir, new_cluster.port, os_info.user_shell_arg,
#ifndef WIN32
log_opts.filename
#else
exec_prog(true,
SYSTEMQUOTE "\"%s/psql\" --set ON_ERROR_STOP=on "
/* --no-psqlrc prevents AUTOCOMMIT=off */
- "--no-psqlrc --port %d --username \"%s\" "
+ "--no-psqlrc --port %d --username %s "
"-f \"%s/%s\" --dbname template1 >> \"%s\"" SYSTEMQUOTE,
- new_cluster.bindir, new_cluster.port, os_info.user, os_info.cwd,
+ new_cluster.bindir, new_cluster.port,
+ os_info.user_shell_arg, os_info.cwd,
GLOBALS_DUMP_FILE,
#ifndef WIN32
log_opts.filename
prep_status("Restoring database schema to new cluster");
exec_prog(true,
SYSTEMQUOTE "\"%s/psql\" --set ON_ERROR_STOP=on "
- "--no-psqlrc --port %d --username \"%s\" "
+ "--no-psqlrc --port %d --username %s "
"-f \"%s/%s\" --dbname template1 >> \"%s\"" SYSTEMQUOTE,
- new_cluster.bindir, new_cluster.port, os_info.user, os_info.cwd,
+ new_cluster.bindir, new_cluster.port,
+ os_info.user_shell_arg, os_info.cwd,
DB_DUMP_FILE,
#ifndef WIN32
log_opts.filename
#include <sys/time.h>
#include "libpq-fe.h"
+#include "pqexpbuffer.h"
/* Allocate for null byte */
#define USER_NAME_SIZE 128
const char *progname; /* complete pathname for this program */
char *exec_path; /* full path to my executable */
char *user; /* username for clusters */
+ char *user_shell_arg; /* the same, with shell quoting */
char cwd[MAXPGPATH]; /* current working directory, used for output */
char **tablespaces; /* tablespaces */
int num_tablespaces;
/* util.c */
char *quote_identifier(const char *s);
+extern void appendShellString(PQExpBuffer buf, const char *str);
+extern void appendConnStrVal(PQExpBuffer buf, const char *str);
+extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
int get_user_info(char **user_name);
void check_ok(void);
void report_status(eLogType type, const char *fmt,...);
static PGconn *
get_db_conn(ClusterInfo *cluster, const char *db_name)
{
- char conn_opts[MAXPGPATH];
+ PQExpBufferData conn_opts;
+ PGconn *conn;
- snprintf(conn_opts, sizeof(conn_opts),
- "dbname = '%s' user = '%s' port = %d", db_name, os_info.user,
- cluster->port);
+ /* Build connection string with proper quoting */
+ initPQExpBuffer(&conn_opts);
+ appendPQExpBufferStr(&conn_opts, "dbname=");
+ appendConnStrVal(&conn_opts, db_name);
+ appendPQExpBufferStr(&conn_opts, " user=");
+ appendConnStrVal(&conn_opts, os_info.user);
+ appendPQExpBuffer(&conn_opts, " port=%d", cluster->port);
- return PQconnectdb(conn_opts);
+ conn = PQconnectdb(conn_opts.data);
+ termPQExpBuffer(&conn_opts);
+ return conn;
}
}
+/*
+ * Append the given string to the shell command being built in the buffer,
+ * with suitable shell-style quoting to create exactly one argument.
+ *
+ * Forbid LF or CR characters, which have scant practical use beyond designing
+ * security breaches. The Windows command shell is unusable as a conduit for
+ * arguments containing LF or CR characters. A future major release should
+ * reject those characters in CREATE ROLE and CREATE DATABASE, because use
+ * there eventually leads to errors here.
+ */
+void
+appendShellString(PQExpBuffer buf, const char *str)
+{
+ const char *p;
+
+#ifndef WIN32
+ appendPQExpBufferChar(buf, '\'');
+ for (p = str; *p; p++)
+ {
+ if (*p == '\n' || *p == '\r')
+ {
+ fprintf(stderr,
+ _("shell command argument contains a newline or carriage return: \"%s\"\n"),
+ str);
+ exit(EXIT_FAILURE);
+ }
+
+ if (*p == '\'')
+ appendPQExpBufferStr(buf, "'\"'\"'");
+ else
+ appendPQExpBufferChar(buf, *p);
+ }
+ appendPQExpBufferChar(buf, '\'');
+#else /* WIN32 */
+ int backslash_run_length = 0;
+
+ /*
+ * A Windows system() argument experiences two layers of interpretation.
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
+ * but a caret escapes any byte except LF or CR that would otherwise have
+ * special meaning. Handling of a caret before LF or CR differs between
+ * "cmd.exe /c" and other modes, and it is unusable here.
+ *
+ * Second, the new process parses its command line to construct argv (see
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
+ * backslash-double quote sequences specially.
+ */
+ appendPQExpBufferStr(buf, "^\"");
+ for (p = str; *p; p++)
+ {
+ if (*p == '\n' || *p == '\r')
+ {
+ fprintf(stderr,
+ _("shell command argument contains a newline or carriage return: \"%s\"\n"),
+ str);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
+ if (*p == '"')
+ {
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
+ }
+ appendPQExpBufferStr(buf, "^\\");
+ }
+ else if (*p == '\\')
+ backslash_run_length++;
+ else
+ backslash_run_length = 0;
+
+ /*
+ * Decline to caret-escape the most mundane characters, to ease
+ * debugging and lest we approach the command length limit.
+ */
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ appendPQExpBufferChar(buf, '^');
+ appendPQExpBufferChar(buf, *p);
+ }
+
+ /*
+ * Change N backslashes at end of argument to 2N backslashes, because they
+ * precede the double quote that terminates the argument.
+ */
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
+ }
+ appendPQExpBufferStr(buf, "^\"");
+#endif /* WIN32 */
+}
+
+
+/*
+ * Append the given string to the buffer, with suitable quoting for passing
+ * the string as a value, in a keyword/pair value in a libpq connection
+ * string
+ */
+void
+appendConnStrVal(PQExpBuffer buf, const char *str)
+{
+ const char *s;
+ bool needquotes;
+
+ /*
+ * If the string is one or more plain ASCII characters, no need to quote
+ * it. This is quite conservative, but better safe than sorry.
+ */
+ needquotes = true;
+ for (s = str; *s; s++)
+ {
+ if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ {
+ needquotes = true;
+ break;
+ }
+ needquotes = false;
+ }
+
+ if (needquotes)
+ {
+ appendPQExpBufferChar(buf, '\'');
+ while (*str)
+ {
+ /* ' and \ must be escaped by to \' and \\ */
+ if (*str == '\'' || *str == '\\')
+ appendPQExpBufferChar(buf, '\\');
+
+ appendPQExpBufferChar(buf, *str);
+ str++;
+ }
+ appendPQExpBufferChar(buf, '\'');
+ }
+ else
+ appendPQExpBufferStr(buf, str);
+}
+
+
+/*
+ * Append a psql meta-command that connects to the given database with the
+ * then-current connection's user, host and port.
+ */
+void
+appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
+{
+ const char *s;
+ bool complex;
+
+ /*
+ * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
+ * For other names, even many not technically requiring it, skip to the
+ * general case. No database has a zero-length name.
+ */
+ complex = false;
+ for (s = dbname; *s; s++)
+ {
+ if (*s == '\n' || *s == '\r')
+ {
+ fprintf(stderr,
+ _("database name contains a newline or carriage return: \"%s\"\n"),
+ dbname);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ {
+ complex = true;
+ }
+ }
+
+ appendPQExpBufferStr(buf, "\\connect ");
+ if (complex)
+ {
+ PQExpBufferData connstr;
+
+ initPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, dbname);
+
+ appendPQExpBuffer(buf, "-reuse-previous=on ");
+
+ /*
+ * As long as the name does not contain a newline, SQL identifier
+ * quoting satisfies the psql meta-command parser. Prefer not to
+ * involve psql-interpreted single quotes, which behaved differently
+ * before PostgreSQL 9.2.
+ */
+ appendPQExpBufferStr(buf, quote_identifier(connstr.data));
+
+ termPQExpBuffer(&connstr);
+ }
+ else
+ appendPQExpBufferStr(buf, quote_identifier(dbname));
+ appendPQExpBufferChar(buf, '\n');
+}
+
+
/*
* get_user_info()
* (copied from initdb.c) find the current user
found = true;
if (!check_mode)
{
+ PQExpBufferData connectbuf;
+
if (script == NULL && (script = fopen(output_path, "w")) == NULL)
pg_log(PG_FATAL, "could not create necessary file: %s\n", output_path);
- fprintf(script, "\\connect %s\n",
- quote_identifier(active_db->db_name));
+
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, active_db->db_name);
+ fputs(connectbuf.data, script);
+ termPQExpBuffer(&connectbuf);
+
fprintf(script,
"SELECT pg_catalog.lo_create(t.loid)\n"
"FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) AS t;\n");
pg_log(PG_FATAL, "could not create necessary file: %s\n", output_path);
if (!db_used)
{
- fprintf(script, "\\connect %s\n\n",
- quote_identifier(active_db->db_name));
+ PQExpBufferData connectbuf;
+
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, active_db->db_name);
+ appendPQExpBufferChar(&connectbuf, '\n');
+ fputs(connectbuf.data, script);
+ termPQExpBuffer(&connectbuf);
db_used = true;
}
pg_log(PG_FATAL, "could not create necessary file: %s\n", output_path);
if (!db_used)
{
- fprintf(script, "\\connect %s\n",
- quote_identifier(active_db->db_name));
+ PQExpBufferData connectbuf;
+
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, active_db->db_name);
+ fputs(connectbuf.data, script);
+ termPQExpBuffer(&connectbuf);
db_used = true;
}
fprintf(script, "REINDEX INDEX %s.%s;\n",
pg_log(PG_FATAL, "could not create necessary file: %s\n", output_path);
if (!db_used)
{
- fprintf(script, "\\connect %s\n",
- quote_identifier(active_db->db_name));
+ PQExpBufferData connectbuf;
+
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, active_db->db_name);
+ fputs(connectbuf.data, script);
+ termPQExpBuffer(&connectbuf);
db_used = true;
}
fprintf(script, "REINDEX INDEX %s.%s;\n",
pg_log(PG_FATAL, "could not create necessary file: %s\n", output_path);
if (!db_used)
{
- fprintf(script, "\\connect %s\n\n",
- quote_identifier(active_db->db_name));
+ PQExpBufferData connectbuf;
+
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, active_db->db_name);
+ appendPQExpBufferChar(&connectbuf, '\n');
+ fputs(connectbuf.data, script);
+ termPQExpBuffer(&connectbuf);
db_used = true;
}
}
+/*
+ * Append the given string to the shell command being built in the buffer,
+ * with suitable shell-style quoting to create exactly one argument.
+ *
+ * Forbid LF or CR characters, which have scant practical use beyond designing
+ * security breaches. The Windows command shell is unusable as a conduit for
+ * arguments containing LF or CR characters. A future major release should
+ * reject those characters in CREATE ROLE and CREATE DATABASE, because use
+ * there eventually leads to errors here.
+ */
+void
+appendShellString(PQExpBuffer buf, const char *str)
+{
+ const char *p;
+
+#ifndef WIN32
+ appendPQExpBufferChar(buf, '\'');
+ for (p = str; *p; p++)
+ {
+ if (*p == '\n' || *p == '\r')
+ {
+ fprintf(stderr,
+ _("shell command argument contains a newline or carriage return: \"%s\"\n"),
+ str);
+ exit(EXIT_FAILURE);
+ }
+
+ if (*p == '\'')
+ appendPQExpBufferStr(buf, "'\"'\"'");
+ else
+ appendPQExpBufferChar(buf, *p);
+ }
+ appendPQExpBufferChar(buf, '\'');
+#else /* WIN32 */
+ int backslash_run_length = 0;
+
+ /*
+ * A Windows system() argument experiences two layers of interpretation.
+ * First, cmd.exe interprets the string. Its behavior is undocumented,
+ * but a caret escapes any byte except LF or CR that would otherwise have
+ * special meaning. Handling of a caret before LF or CR differs between
+ * "cmd.exe /c" and other modes, and it is unusable here.
+ *
+ * Second, the new process parses its command line to construct argv (see
+ * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
+ * backslash-double quote sequences specially.
+ */
+ appendPQExpBufferStr(buf, "^\"");
+ for (p = str; *p; p++)
+ {
+ if (*p == '\n' || *p == '\r')
+ {
+ fprintf(stderr,
+ _("shell command argument contains a newline or carriage return: \"%s\"\n"),
+ str);
+ exit(EXIT_FAILURE);
+ }
+
+ /* Change N backslashes before a double quote to 2N+1 backslashes. */
+ if (*p == '"')
+ {
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
+ }
+ appendPQExpBufferStr(buf, "^\\");
+ }
+ else if (*p == '\\')
+ backslash_run_length++;
+ else
+ backslash_run_length = 0;
+
+ /*
+ * Decline to caret-escape the most mundane characters, to ease
+ * debugging and lest we approach the command length limit.
+ */
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ appendPQExpBufferChar(buf, '^');
+ appendPQExpBufferChar(buf, *p);
+ }
+
+ /*
+ * Change N backslashes at end of argument to 2N backslashes, because they
+ * precede the double quote that terminates the argument.
+ */
+ while (backslash_run_length)
+ {
+ appendPQExpBufferStr(buf, "^\\");
+ backslash_run_length--;
+ }
+ appendPQExpBufferStr(buf, "^\"");
+#endif /* WIN32 */
+}
+
+
+/*
+ * Append the given string to the buffer, with suitable quoting for passing
+ * the string as a value, in a keyword/pair value in a libpq connection
+ * string
+ */
+void
+appendConnStrVal(PQExpBuffer buf, const char *str)
+{
+ const char *s;
+ bool needquotes;
+
+ /*
+ * If the string is one or more plain ASCII characters, no need to quote
+ * it. This is quite conservative, but better safe than sorry.
+ */
+ needquotes = true;
+ for (s = str; *s; s++)
+ {
+ if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ {
+ needquotes = true;
+ break;
+ }
+ needquotes = false;
+ }
+
+ if (needquotes)
+ {
+ appendPQExpBufferChar(buf, '\'');
+ while (*str)
+ {
+ /* ' and \ must be escaped by to \' and \\ */
+ if (*str == '\'' || *str == '\\')
+ appendPQExpBufferChar(buf, '\\');
+
+ appendPQExpBufferChar(buf, *str);
+ str++;
+ }
+ appendPQExpBufferChar(buf, '\'');
+ }
+ else
+ appendPQExpBufferStr(buf, str);
+}
+
+
+/*
+ * Append a psql meta-command that connects to the given database with the
+ * then-current connection's user, host and port.
+ */
+void
+appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
+{
+ const char *s;
+ bool complex;
+
+ /*
+ * If the name is plain ASCII characters, emit a trivial "\connect "foo"".
+ * For other names, even many not technically requiring it, skip to the
+ * general case. No database has a zero-length name.
+ */
+ complex = false;
+ for (s = dbname; *s; s++)
+ {
+ if (*s == '\n' || *s == '\r')
+ {
+ fprintf(stderr,
+ _("database name contains a newline or carriage return: \"%s\"\n"),
+ dbname);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') ||
+ (*s >= '0' && *s <= '9') || *s == '_' || *s == '.'))
+ {
+ complex = true;
+ }
+ }
+
+ appendPQExpBufferStr(buf, "\\connect ");
+ if (complex)
+ {
+ PQExpBufferData connstr;
+
+ initPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, dbname);
+
+ appendPQExpBuffer(buf, "-reuse-previous=on ");
+
+ /*
+ * As long as the name does not contain a newline, SQL identifier
+ * quoting satisfies the psql meta-command parser. Prefer not to
+ * involve psql-interpreted single quotes, which behaved differently
+ * before PostgreSQL 9.2.
+ */
+ appendPQExpBufferStr(buf, fmtId(connstr.data));
+
+ termPQExpBuffer(&connstr);
+ }
+ else
+ appendPQExpBufferStr(buf, fmtId(dbname));
+ appendPQExpBufferChar(buf, '\n');
+}
+
+
/*
* Convert a bytea value (presented as raw bytes) to an SQL string literal
* and append it to the given buffer. We assume the specified
const unsigned char *str, size_t length,
bool std_strings);
extern int parse_version(const char *versionString);
+extern void appendShellString(PQExpBuffer buf, const char *str);
+extern void appendConnStrVal(PQExpBuffer buf, const char *str);
+extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
extern bool buildACLCommands(const char *name, const char *subname,
const char *type, const char *acls, const char *owner,
char *triggerNames;
int useDB;
- char *dbname;
+ char *dbname; /* subject to expand_dbname */
char *pgport;
char *pghost;
char *username;
/* If we created a DB, connect to it... */
if (strcmp(te->desc, "DATABASE") == 0)
{
+ PQExpBufferData connstr;
+
+ initPQExpBuffer(&connstr);
+ appendPQExpBufferStr(&connstr, "dbname=");
+ appendConnStrVal(&connstr, te->tag);
+ /* Abandon struct, but keep its buffer until process exit. */
+
ahlog(AH, 1, "connecting to new database \"%s\"\n", te->tag);
_reconnectToDB(AH, te->tag);
- ropt->dbname = strdup(te->tag);
+ ropt->dbname = connstr.data;
}
}
ReconnectToServer(AH, dbname, NULL);
else
{
- PQExpBuffer qry = createPQExpBuffer();
+ if (dbname)
+ {
+ PQExpBufferData connectbuf;
- appendPQExpBuffer(qry, "\\connect %s\n\n",
- dbname ? fmtId(dbname) : "-");
- ahprintf(AH, "%s", qry->data);
- destroyPQExpBuffer(qry);
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, dbname);
+ ahprintf(AH, "%s\n", connectbuf.data);
+ termPQExpBuffer(&connectbuf);
+ }
+ else
+ ahprintf(AH, "%s\n", "\\connect -\n");
}
/*
static PGconn *
_connectDB(ArchiveHandle *AH, const char *reqdb, const char *requser)
{
+ PQExpBufferData connstr;
PGconn *newConn;
const char *newdb;
const char *newuser;
die_horribly(AH, modulename, "out of memory\n");
}
+ initPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, newdb);
+
do
{
#define PARAMS_ARRAY_SIZE 7
keywords[3] = "password";
values[3] = password;
keywords[4] = "dbname";
- values[4] = newdb;
+ values[4] = connstr.data;
keywords[5] = "fallback_application_name";
values[5] = progname;
keywords[6] = NULL;
AH->savedPassword = password;
+ termPQExpBuffer(&connstr);
+
/* check for version mismatch */
_check_database_version(AH);
const char *name2);
static void dumpDatabases(PGconn *conn);
static void dumpTimestamp(char *msg);
-static void appendShellString(PQExpBuffer buf, const char *str);
-static void appendConnStrVal(PQExpBuffer buf, const char *str);
static int runPgDump(const char *dbname);
static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
fdbname, fmtId(dbtablespace));
/* connect to original database */
- appendPQExpBuffer(buf, "%s\\connect %s\n",
- binary_upgrade ? " " : "", fdbname);
+ if (binary_upgrade)
+ appendPQExpBufferChar(buf, ' ');
+ appendPsqlMetaConnect(buf, dbname);
}
if (binary_upgrade)
int ret;
char *dbname = PQgetvalue(res, i, 0);
+ PQExpBufferData connectbuf;
if (verbose)
fprintf(stderr, _("%s: dumping database \"%s\"...\n"), progname, dbname);
- fprintf(OPF, "\\connect %s\n\n", fmtId(dbname));
+ initPQExpBuffer(&connectbuf);
+ appendPsqlMetaConnect(&connectbuf, dbname);
+ fprintf(OPF, "%s\n", connectbuf.data);
+ termPQExpBuffer(&connectbuf);
/*
* Restore will need to write to the target cluster. This connection
localtime(&now)) != 0)
fprintf(OPF, "-- %s %s\n\n", msg, buf);
}
-
-
-/*
- * Append the given string to the buffer, with suitable quoting for passing
- * the string as a value, in a keyword/pair value in a libpq connection
- * string
- */
-static void
-appendConnStrVal(PQExpBuffer buf, const char *str)
-{
- const char *s;
- bool needquotes;
-
- /*
- * If the string consists entirely of plain ASCII characters, no need to
- * quote it. This is quite conservative, but better safe than sorry.
- */
- 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)
- {
- appendPQExpBufferChar(buf, '\'');
- while (*str)
- {
- /* ' and \ must be escaped by to \' and \\ */
- if (*str == '\'' || *str == '\\')
- appendPQExpBufferChar(buf, '\\');
-
- appendPQExpBufferChar(buf, *str);
- str++;
- }
- appendPQExpBufferChar(buf, '\'');
- }
- else
- appendPQExpBufferStr(buf, str);
-}
-
-/*
- * Append the given string to the shell command being built in the buffer,
- * with suitable shell-style quoting to create exactly one argument.
- *
- * Forbid LF or CR characters, which have scant practical use beyond designing
- * security breaches. The Windows command shell is unusable as a conduit for
- * arguments containing LF or CR characters. A future major release should
- * reject those characters in CREATE ROLE and CREATE DATABASE, because use
- * there eventually leads to errors here.
- */
-static void
-appendShellString(PQExpBuffer buf, const char *str)
-{
- const char *p;
-
-#ifndef WIN32
- appendPQExpBufferChar(buf, '\'');
- for (p = str; *p; p++)
- {
- if (*p == '\n' || *p == '\r')
- {
- fprintf(stderr,
- _("shell command argument contains a newline or carriage return: \"%s\"\n"),
- str);
- exit(EXIT_FAILURE);
- }
-
- if (*p == '\'')
- appendPQExpBuffer(buf, "'\"'\"'");
- else
- appendPQExpBufferChar(buf, *p);
- }
- appendPQExpBufferChar(buf, '\'');
-#else /* WIN32 */
- int backslash_run_length = 0;
-
- /*
- * A Windows system() argument experiences two layers of interpretation.
- * First, cmd.exe interprets the string. Its behavior is undocumented,
- * but a caret escapes any byte except LF or CR that would otherwise have
- * special meaning. Handling of a caret before LF or CR differs between
- * "cmd.exe /c" and other modes, and it is unusable here.
- *
- * Second, the new process parses its command line to construct argv (see
- * https://msdn.microsoft.com/en-us/library/17w5ykft.aspx). This treats
- * backslash-double quote sequences specially.
- */
- appendPQExpBufferStr(buf, "^\"");
- for (p = str; *p; p++)
- {
- if (*p == '\n' || *p == '\r')
- {
- fprintf(stderr,
- _("shell command argument contains a newline or carriage return: \"%s\"\n"),
- str);
- exit(EXIT_FAILURE);
- }
-
- /* Change N backslashes before a double quote to 2N+1 backslashes. */
- if (*p == '"')
- {
- while (backslash_run_length)
- {
- appendPQExpBufferStr(buf, "^\\");
- backslash_run_length--;
- }
- appendPQExpBufferStr(buf, "^\\");
- }
- else if (*p == '\\')
- backslash_run_length++;
- else
- backslash_run_length = 0;
-
- /*
- * Decline to caret-escape the most mundane characters, to ease
- * debugging and lest we approach the command length limit.
- */
- if (!((*p >= 'a' && *p <= 'z') ||
- (*p >= 'A' && *p <= 'Z') ||
- (*p >= '0' && *p <= '9')))
- appendPQExpBufferChar(buf, '^');
- appendPQExpBufferChar(buf, *p);
- }
-
- /*
- * Change N backslashes at end of argument to 2N backslashes, because they
- * precede the double quote that terminates the argument.
- */
- while (backslash_run_length)
- {
- appendPQExpBufferStr(buf, "^\\");
- backslash_run_length--;
- }
- appendPQExpBufferStr(buf, "^\"");
-#endif /* WIN32 */
-}
bool keep_password;
bool has_connection_string;
bool reuse_previous;
+ PQExpBufferData connstr;
has_connection_string = dbname ?
recognized_connection_string(dbname) : false;
* changes: passwords aren't (usually) database-specific.
*/
if (!dbname && reuse_previous)
- dbname = PQdb(o_conn);
+ {
+ initPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, PQdb(o_conn));
+ dbname = connstr.data;
+ /* has_connection_string=true would be a dead store */
+ }
+ else
+ connstr.data = NULL;
/*
* If the user asked to be prompted for a password, ask for one now. If
}
PQfinish(n_conn);
+ if (connstr.data)
+ termPQExpBuffer(&connstr);
return false;
}
+ if (connstr.data)
+ termPQExpBuffer(&connstr);
/*
* Replace the old connection with the new one, and update
droplang: droplang.o common.o print.o mbprint.o | submake-libpq
dropuser: dropuser.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq
clusterdb: clusterdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq
-vacuumdb: vacuumdb.o common.o | submake-libpq
+vacuumdb: vacuumdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq
reindexdb: reindexdb.o common.o dumputils.o kwlookup.o keywords.o | submake-libpq
dumputils.c keywords.c: % : $(top_srcdir)/src/bin/pg_dump/%
{
PGconn *conn;
PGresult *result;
+ PQExpBufferData connstr;
int i;
conn = connectDatabase("postgres", host, port, username, prompt_password, progname);
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo);
PQfinish(conn);
+ initPQExpBuffer(&connstr);
for (i = 0; i < PQntuples(result); i++)
{
char *dbname = PQgetvalue(result, i, 0);
fflush(stdout);
}
- cluster_one_database(dbname, verbose, NULL,
+ resetPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, dbname);
+
+ cluster_one_database(connstr.data, verbose, NULL,
host, port, username, prompt_password,
progname, echo);
}
+ termPQExpBuffer(&connstr);
PQclear(result);
}
{
PGconn *conn;
PGresult *result;
+ PQExpBufferData connstr;
int i;
conn = connectDatabase("postgres", host, port, username, prompt_password, progname);
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo);
PQfinish(conn);
+ initPQExpBuffer(&connstr);
for (i = 0; i < PQntuples(result); i++)
{
char *dbname = PQgetvalue(result, i, 0);
fflush(stdout);
}
- reindex_one_database(dbname, dbname, "DATABASE", host, port, username,
- prompt_password, progname, echo);
+ resetPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, dbname);
+
+ reindex_one_database(NULL, connstr.data, "DATABASE", host,
+ port, username, prompt_password,
+ progname, echo);
}
+ termPQExpBuffer(&connstr);
PQclear(result);
}
initPQExpBuffer(&sql);
- appendPQExpBuffer(&sql, "REINDEX SYSTEM %s;\n", PQdb(conn));
+ appendPQExpBuffer(&sql, "REINDEX SYSTEM %s;\n", fmtId(PQdb(conn)));
if (!executeMaintenanceCommand(conn, sql.data, echo))
{
#include "postgres_fe.h"
#include "common.h"
+#include "dumputils.h"
static void vacuum_one_database(const char *dbname, bool full, bool verbose,
{
PGconn *conn;
PGresult *result;
+ PQExpBufferData connstr;
int i;
conn = connectDatabase("postgres", host, port, username, prompt_password, progname);
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo);
PQfinish(conn);
+ initPQExpBuffer(&connstr);
for (i = 0; i < PQntuples(result); i++)
{
char *dbname = PQgetvalue(result, i, 0);
fflush(stdout);
}
- vacuum_one_database(dbname, full, verbose, and_analyze, analyze_only,
+ resetPQExpBuffer(&connstr);
+ appendPQExpBuffer(&connstr, "dbname=");
+ appendConnStrVal(&connstr, PQgetvalue(result, i, 0));
+
+ vacuum_one_database(connstr.data, full, verbose, and_analyze,
+ analyze_only,
freeze, NULL, host, port, username, prompt_password,
progname, echo);
}
+ termPQExpBuffer(&connstr);
PQclear(result);
}
* Defaults are supplied (from a service file, environment variables, etc)
* for unspecified options, but only if use_defaults is TRUE.
*
- * If expand_dbname is non-zero, and the value passed for keyword "dbname"
- * contains an "=", assume it is a conninfo string and process it,
- * overriding any previously processed conflicting keywords. Subsequent
- * keywords will take precedence, however.
+ * If expand_dbname is non-zero, and the value passed for the first occurrence
+ * of "dbname" keyword contains an "=", assume it is a conninfo string and
+ * process it, overriding any previously processed conflicting
+ * keywords. Subsequent keywords will take precedence, however. In-tree
+ * programs generally specify expand_dbname=true, so command-line arguments
+ * naming a database can use a connection string. Some code acquires
+ * arbitrary database names from known-literal sources like PQdb(),
+ * PQconninfoParse() and pg_database.datname. When connecting to such a
+ * database, in-tree code first wraps the name in a connection string.
*/
static PQconninfoOption *
conninfo_array_parse(const char **keywords, const char **values,