From 15c30b6f9ade42754b6721c64ba1766f16252107 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 11 Feb 2002 00:18:20 +0000
Subject: [PATCH] Be more wary about mixed-case database names and user names. 
 Get the CREATE DATABASE command right in pg_dump -C case.

---
 src/bin/pg_dump/common.c             | 64 +------------------
 src/bin/pg_dump/pg_backup.h          |  9 +--
 src/bin/pg_dump/pg_backup_archiver.c | 95 +++++++++++++++++++++++++---
 src/bin/pg_dump/pg_dump.c            | 48 ++++++++++----
 src/bin/pg_dump/pg_dump.h            |  3 +-
 src/bin/pg_dump/pg_dumpall.sh        |  6 +-
 6 files changed, 131 insertions(+), 94 deletions(-)

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 1a32e22b22b..907590518cf 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.61 2002/01/11 23:21:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.62 2002/02/11 00:18:20 tgl Exp $
  *
  * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2
  *
@@ -590,65 +590,3 @@ findFuncByName(FuncInfo *finfo, int numFuncs, const char *name)
 	}
 	return -1;
 }
-
-/*
- * fmtId
- *
- *	checks input string for non-lowercase characters
- *	returns pointer to input string or string surrounded by double quotes
- *
- *	Note that the returned string should be used immediately since it
- *	uses a static buffer to hold the string. Non-reentrant but faster?
- */
-const char *
-fmtId(const char *rawid, bool force_quotes)
-{
-	static PQExpBuffer id_return = NULL;
-	const char *cp;
-
-	if (!force_quotes)
-	{
-		/* do a quick check on the first character... */
-		if (!islower((unsigned char) *rawid))
-			force_quotes = true;
-		/* otherwise check the entire string */
-		else
-			for (cp = rawid; *cp; cp++)
-			{
-				if (!(islower((unsigned char) *cp) ||
-					  isdigit((unsigned char) *cp) ||
-					  (*cp == '_')))
-				{
-					force_quotes = true;
-					break;
-				}
-			}
-	}
-
-	if (!force_quotes)
-		return rawid;			/* no quoting needed */
-
-	if (id_return)
-		resetPQExpBuffer(id_return);
-	else
-		id_return = createPQExpBuffer();
-
-	appendPQExpBufferChar(id_return, '\"');
-	for (cp = rawid; *cp; cp++)
-	{
-		/*
-		 * Did we find a double-quote in the string? Then make this a
-		 * double double-quote per SQL99. Before, we put in a
-		 * backslash/double-quote pair. - thomas 2000-08-05
-		 */
-		if (*cp == '\"')
-		{
-			appendPQExpBufferChar(id_return, '\"');
-			appendPQExpBufferChar(id_return, '\"');
-		}
-		appendPQExpBufferChar(id_return, *cp);
-	}
-	appendPQExpBufferChar(id_return, '\"');
-
-	return id_return->data;
-}	/* fmtId() */
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index b81b029e293..32d4c1e88eb 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup.h,v 1.17 2001/10/28 06:25:58 momjian Exp $
+ *		$Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup.h,v 1.18 2002/02/11 00:18:20 tgl Exp $
  *
  * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
  *
@@ -136,10 +136,11 @@ extern void
 exit_horribly(Archive *AH, const char *modulename, const char *fmt,...)
 __attribute__((format(printf, 3, 4)));
 
-extern char *
-			simple_prompt(const char *prompt, int maxlen, bool echo);
+extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
 
-/* Lets the archibe know we have a DB connection to shutdown if it dies */
+extern const char *fmtId(const char *identifier, bool force_quotes);
+
+/* Lets the archive know we have a DB connection to shutdown if it dies */
 
 PGconn *ConnectDatabase(Archive *AH,
 				const char *dbname,
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index baebc56e4d4..2c8ea7d828f 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.41 2002/02/06 17:27:50 tgl Exp $
+ *		$Header: /cvsroot/pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.42 2002/02/11 00:18:20 tgl Exp $
  *
  * Modifications - 28-Jun-2000 - pjw@rhyme.com.au
  *
@@ -74,6 +74,7 @@
 #include "pg_backup_archiver.h"
 #include "pg_backup_db.h"
 
+#include <ctype.h>
 #include <errno.h>
 #include <unistd.h>				/* for dup */
 
@@ -1953,7 +1954,7 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt)
  * user, this won't do anything.
  *
  * If we're currently restoring right into a database, this will
- * actuall establish a connection.	Otherwise it puts a \connect into
+ * actually establish a connection.	Otherwise it puts a \connect into
  * the script output.
  */
 static void
@@ -1974,7 +1975,8 @@ _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user)
 			PQExpBuffer qry = createPQExpBuffer();
 			PGresult   *res;
 
-			appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION '%s';", user);
+			appendPQExpBuffer(qry, "SET SESSION AUTHORIZATION %s;",
+							  fmtId(user, false));
 			res = PQexec(AH->connection, qry->data);
 
 			if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
@@ -1985,19 +1987,29 @@ _reconnectAsUser(ArchiveHandle *AH, const char *dbname, const char *user)
 			destroyPQExpBuffer(qry);
 		}
 		else
-			ahprintf(AH, "SET SESSION AUTHORIZATION '%s';\n\n", user);
+			ahprintf(AH, "SET SESSION AUTHORIZATION %s;\n\n",
+					 fmtId(user, false));
 	}
-	/* When -R was given, don't do anything. */
 	else if (AH->ropt && AH->ropt->noReconnect)
+	{
+		/* When -R was given, don't do anything. */
 		return;
-
+	}
 	else if (RestoringToDB(AH))
 		ReconnectToServer(AH, dbname, user);
 	else
-		/* FIXME: does not handle mixed case user names */
-		ahprintf(AH, "\\connect %s %s\n\n",
-				 dbname ? dbname : "-",
-				 user ? user : "-");
+	{
+		PQExpBuffer qry = createPQExpBuffer();
+
+		appendPQExpBuffer(qry, "\\connect %s",
+						  dbname ? fmtId(dbname, false) : "-");
+		appendPQExpBuffer(qry, " %s\n\n",
+						  fmtId(user, false));
+
+		ahprintf(AH, qry->data);
+
+		destroyPQExpBuffer(qry);
+	}
 
 	/*
 	 * NOTE: currUser keeps track of what the imaginary session user in
@@ -2025,6 +2037,69 @@ _reconnectAsOwner(ArchiveHandle *AH, const char *dbname, TocEntry *te)
 }
 
 
+/*
+ * fmtId
+ *
+ *	checks input string for non-lowercase characters
+ *	returns pointer to input string or string surrounded by double quotes
+ *
+ *	Note that the returned string should be used immediately since it
+ *	uses a static buffer to hold the string. Non-reentrant but faster?
+ */
+const char *
+fmtId(const char *rawid, bool force_quotes)
+{
+	static PQExpBuffer id_return = NULL;
+	const char *cp;
+
+	if (!force_quotes)
+	{
+		/* do a quick check on the first character... */
+		if (!islower((unsigned char) *rawid))
+			force_quotes = true;
+		/* otherwise check the entire string */
+		else
+			for (cp = rawid; *cp; cp++)
+			{
+				if (!(islower((unsigned char) *cp) ||
+					  isdigit((unsigned char) *cp) ||
+					  (*cp == '_')))
+				{
+					force_quotes = true;
+					break;
+				}
+			}
+	}
+
+	if (!force_quotes)
+		return rawid;			/* no quoting needed */
+
+	if (id_return)
+		resetPQExpBuffer(id_return);
+	else
+		id_return = createPQExpBuffer();
+
+	appendPQExpBufferChar(id_return, '\"');
+	for (cp = rawid; *cp; cp++)
+	{
+		/*
+		 * Did we find a double-quote in the string? Then make this a
+		 * double double-quote per SQL99. Before, we put in a
+		 * backslash/double-quote pair. - thomas 2000-08-05
+		 */
+		if (*cp == '\"')
+		{
+			appendPQExpBufferChar(id_return, '\"');
+			appendPQExpBufferChar(id_return, '\"');
+		}
+		appendPQExpBufferChar(id_return, *cp);
+	}
+	appendPQExpBufferChar(id_return, '\"');
+
+	return id_return->data;
+}
+
+
 static int
 _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isData)
 {
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 0eb30b58d16..64b4887e29c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.240 2002/02/06 17:27:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.241 2002/02/11 00:18:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1141,15 +1141,24 @@ dumpDatabase(Archive *AH)
 	PQExpBuffer creaQry = createPQExpBuffer();
 	PGresult   *res;
 	int			ntups;
-	int			i_dba;
+	int			i_dba,
+				i_encoding,
+				i_datpath;
+	const char *datname,
+			   *dba,
+			   *encoding,
+			   *datpath;
+
+	datname = PQdb(g_conn);
 
 	if (g_verbose)
 		write_msg(NULL, "saving database definition\n");
 
-	/* Get the dba */
-	appendPQExpBuffer(dbQry, "select (select usename from pg_user where datdba = usesysid) as dba from pg_database"
+	/* Get the database owner and parameters from pg_database */
+	appendPQExpBuffer(dbQry, "select (select usename from pg_user where usesysid = datdba) as dba,"
+					  " encoding, datpath from pg_database"
 					  " where datname = ");
-	formatStringLiteral(dbQry, PQdb(g_conn), CONV_ALL);
+	formatStringLiteral(dbQry, datname, CONV_ALL);
 
 	res = PQexec(g_conn, dbQry->data);
 	if (!res ||
@@ -1165,24 +1174,39 @@ dumpDatabase(Archive *AH)
 
 	if (ntups <= 0)
 	{
-		write_msg(NULL, "missing pg_database entry for database \"%s\"\n", PQdb(g_conn));
+		write_msg(NULL, "missing pg_database entry for database \"%s\"\n",
+				  datname);
 		exit_nicely();
 	}
 
 	if (ntups != 1)
 	{
 		write_msg(NULL, "query returned more than one (%d) pg_database entry for database \"%s\"\n",
-				  ntups, PQdb(g_conn));
+				  ntups, datname);
 		exit_nicely();
 	}
 
-	appendPQExpBuffer(creaQry, "Create Database \"%s\";\n", PQdb(g_conn));
-	appendPQExpBuffer(delQry, "Drop Database \"%s\";\n", PQdb(g_conn));
 	i_dba = PQfnumber(res, "dba");
-
-	ArchiveEntry(AH, "0" /* OID */ , PQdb(g_conn) /* Name */ , "DATABASE", NULL,
+	i_encoding = PQfnumber(res, "encoding");
+	i_datpath = PQfnumber(res, "datpath");
+	dba = PQgetvalue(res, 0, i_dba);
+	encoding = PQgetvalue(res, 0, i_encoding);
+	datpath = PQgetvalue(res, 0, i_datpath);
+
+	appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
+					  fmtId(datname, force_quotes));
+	if (strlen(encoding) > 0)
+		appendPQExpBuffer(creaQry, " ENCODING = %s", encoding);
+	if (strlen(datpath) > 0)
+		appendPQExpBuffer(creaQry, " LOCATION = '%s'", datpath);
+	appendPQExpBuffer(creaQry, ";\n");
+
+	appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
+					  fmtId(datname, force_quotes));
+
+	ArchiveEntry(AH, "0" /* OID */ , datname /* Name */ , "DATABASE", NULL,
 				 creaQry->data /* Create */ , delQry->data /* Del */ ,
-				 "" /* Copy */ , PQgetvalue(res, 0, i_dba) /* Owner */ ,
+				 "" /* Copy */ , dba /* Owner */ ,
 				 NULL /* Dumper */ , NULL /* Dumper Arg */ );
 
 	PQclear(res);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 3af39b8d2e5..0d69fda200b 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_dump.h,v 1.77 2002/01/11 23:21:55 tgl Exp $
+ * $Id: pg_dump.h,v 1.78 2002/02/11 00:18:20 tgl Exp $
  *
  * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2
  *
@@ -280,7 +280,6 @@ extern void dumpTables(Archive *fout, TableInfo *tbinfo, int numTables,
 		   const bool schemaOnly, const bool dataOnly);
 extern void dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes,
 			TableInfo *tbinfo, int numTables, const char *tablename);
-extern const char *fmtId(const char *identifier, bool force_quotes);
 extern void exit_nicely(void);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dumpall.sh b/src/bin/pg_dump/pg_dumpall.sh
index 122cf75b2dc..7aab5e2d766 100644
--- a/src/bin/pg_dump/pg_dumpall.sh
+++ b/src/bin/pg_dump/pg_dumpall.sh
@@ -6,7 +6,7 @@
 # and "pg_group" tables, which belong to the whole installation rather
 # than any one individual database.
 #
-# $Header: /cvsroot/pgsql/src/bin/pg_dump/Attic/pg_dumpall.sh,v 1.14 2002/01/09 04:56:44 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/pg_dump/Attic/pg_dumpall.sh,v 1.15 2002/02/11 00:18:20 tgl Exp $
 
 CMDNAME=`basename $0`
 
@@ -217,7 +217,7 @@ while read DATABASE DBOWNER ENCODING ISTEMPLATE DBPATH; do
     echo "--"
     echo "-- Database $DATABASE"
     echo "--"
-    echo "${BS}connect template1 $DBOWNER"
+    echo "${BS}connect template1 \"$DBOWNER\""
 
     if [ "$cleanschema" = yes -a "$DATABASE" != template1 ] ; then
         echo "DROP DATABASE \"$DATABASE\";"
@@ -234,7 +234,7 @@ while read DATABASE DBOWNER ENCODING ISTEMPLATE DBPATH; do
 	echo "$createdbcmd;"
     fi
 
-    echo "${BS}connect $DATABASE $DBOWNER"
+    echo "${BS}connect \"$DATABASE\" \"$DBOWNER\""
     echo "dumping database \"$DATABASE\"..." 1>&2
     $PGDUMP "$DATABASE" <&4
     if [ "$?" -ne 0 ] ; then
-- 
GitLab